refactor(background-agent): optimize cache timer lifecycle and result handling
Improve cache timer management in background agent manager and update result handler to properly handle cache state transitions
This commit is contained in:
parent
9f84da1d35
commit
159fccddcf
@ -2157,6 +2157,67 @@ describe("BackgroundManager.completionTimers - Memory Leak Fix", () => {
|
|||||||
manager.shutdown()
|
manager.shutdown()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("should start cleanup timers only after all tasks complete", async () => {
|
||||||
|
// given
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: async () => ({}),
|
||||||
|
abort: async () => ({}),
|
||||||
|
messages: async () => ({ data: [] }),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput)
|
||||||
|
const taskA: BackgroundTask = {
|
||||||
|
id: "task-timer-a",
|
||||||
|
sessionID: "session-timer-a",
|
||||||
|
parentSessionID: "parent-session",
|
||||||
|
parentMessageID: "msg-a",
|
||||||
|
description: "Task A",
|
||||||
|
prompt: "test",
|
||||||
|
agent: "explore",
|
||||||
|
status: "completed",
|
||||||
|
startedAt: new Date(),
|
||||||
|
completedAt: new Date(),
|
||||||
|
}
|
||||||
|
const taskB: BackgroundTask = {
|
||||||
|
id: "task-timer-b",
|
||||||
|
sessionID: "session-timer-b",
|
||||||
|
parentSessionID: "parent-session",
|
||||||
|
parentMessageID: "msg-b",
|
||||||
|
description: "Task B",
|
||||||
|
prompt: "test",
|
||||||
|
agent: "explore",
|
||||||
|
status: "completed",
|
||||||
|
startedAt: new Date(),
|
||||||
|
completedAt: new Date(),
|
||||||
|
}
|
||||||
|
getTaskMap(manager).set(taskA.id, taskA)
|
||||||
|
getTaskMap(manager).set(taskB.id, taskB)
|
||||||
|
;(manager as unknown as { pendingByParent: Map<string, Set<string>> }).pendingByParent.set(
|
||||||
|
"parent-session",
|
||||||
|
new Set([taskA.id, taskB.id])
|
||||||
|
)
|
||||||
|
|
||||||
|
// when
|
||||||
|
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> })
|
||||||
|
.notifyParentSession(taskA)
|
||||||
|
|
||||||
|
// then
|
||||||
|
const completionTimers = getCompletionTimers(manager)
|
||||||
|
expect(completionTimers.size).toBe(0)
|
||||||
|
|
||||||
|
// when
|
||||||
|
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> })
|
||||||
|
.notifyParentSession(taskB)
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(completionTimers.size).toBe(2)
|
||||||
|
expect(completionTimers.has(taskA.id)).toBe(true)
|
||||||
|
expect(completionTimers.has(taskB.id)).toBe(true)
|
||||||
|
|
||||||
|
manager.shutdown()
|
||||||
|
})
|
||||||
|
|
||||||
test("should clear all completion timers on shutdown", () => {
|
test("should clear all completion timers on shutdown", () => {
|
||||||
// given
|
// given
|
||||||
const manager = createBackgroundManager()
|
const manager = createBackgroundManager()
|
||||||
|
|||||||
@ -1013,9 +1013,11 @@ export class BackgroundManager {
|
|||||||
const errorInfo = task.error ? `\n**Error:** ${task.error}` : ""
|
const errorInfo = task.error ? `\n**Error:** ${task.error}` : ""
|
||||||
|
|
||||||
let notification: string
|
let notification: string
|
||||||
|
let completedTasks: BackgroundTask[] = []
|
||||||
if (allComplete) {
|
if (allComplete) {
|
||||||
const completedTasks = Array.from(this.tasks.values())
|
completedTasks = Array.from(this.tasks.values())
|
||||||
.filter(t => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending")
|
.filter(t => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending")
|
||||||
|
const completedTasksText = completedTasks
|
||||||
.map(t => `- \`${t.id}\`: ${t.description}`)
|
.map(t => `- \`${t.id}\`: ${t.description}`)
|
||||||
.join("\n")
|
.join("\n")
|
||||||
|
|
||||||
@ -1023,7 +1025,7 @@ export class BackgroundManager {
|
|||||||
[ALL BACKGROUND TASKS COMPLETE]
|
[ALL BACKGROUND TASKS COMPLETE]
|
||||||
|
|
||||||
**Completed:**
|
**Completed:**
|
||||||
${completedTasks || `- \`${task.id}\`: ${task.description}`}
|
${completedTasksText || `- \`${task.id}\`: ${task.description}`}
|
||||||
|
|
||||||
Use \`background_output(task_id="<id>")\` to retrieve each result.
|
Use \`background_output(task_id="<id>")\` to retrieve each result.
|
||||||
</system-reminder>`
|
</system-reminder>`
|
||||||
@ -1092,7 +1094,14 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|||||||
log("[background-agent] Failed to send notification:", error)
|
log("[background-agent] Failed to send notification:", error)
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskId = task.id
|
if (allComplete) {
|
||||||
|
for (const completedTask of completedTasks) {
|
||||||
|
const taskId = completedTask.id
|
||||||
|
const existingTimer = this.completionTimers.get(taskId)
|
||||||
|
if (existingTimer) {
|
||||||
|
clearTimeout(existingTimer)
|
||||||
|
this.completionTimers.delete(taskId)
|
||||||
|
}
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
this.completionTimers.delete(taskId)
|
this.completionTimers.delete(taskId)
|
||||||
if (this.tasks.has(taskId)) {
|
if (this.tasks.has(taskId)) {
|
||||||
@ -1103,6 +1112,8 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|||||||
}, TASK_CLEANUP_DELAY_MS)
|
}, TASK_CLEANUP_DELAY_MS)
|
||||||
this.completionTimers.set(taskId, timer)
|
this.completionTimers.set(taskId, timer)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private formatDuration(start: Date, end?: Date): string {
|
private formatDuration(start: Date, end?: Date): string {
|
||||||
const duration = (end ?? new Date()).getTime() - start.getTime()
|
const duration = (end ?? new Date()).getTime() - start.getTime()
|
||||||
|
|||||||
@ -174,9 +174,11 @@ export async function notifyParentSession(
|
|||||||
const errorInfo = task.error ? `\n**Error:** ${task.error}` : ""
|
const errorInfo = task.error ? `\n**Error:** ${task.error}` : ""
|
||||||
|
|
||||||
let notification: string
|
let notification: string
|
||||||
|
let completedTasks: BackgroundTask[] = []
|
||||||
if (allComplete) {
|
if (allComplete) {
|
||||||
const completedTasks = Array.from(state.tasks.values())
|
completedTasks = Array.from(state.tasks.values())
|
||||||
.filter(t => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending")
|
.filter(t => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending")
|
||||||
|
const completedTasksText = completedTasks
|
||||||
.map(t => `- \`${t.id}\`: ${t.description}`)
|
.map(t => `- \`${t.id}\`: ${t.description}`)
|
||||||
.join("\n")
|
.join("\n")
|
||||||
|
|
||||||
@ -184,7 +186,7 @@ export async function notifyParentSession(
|
|||||||
[ALL BACKGROUND TASKS COMPLETE]
|
[ALL BACKGROUND TASKS COMPLETE]
|
||||||
|
|
||||||
**Completed:**
|
**Completed:**
|
||||||
${completedTasks || `- \`${task.id}\`: ${task.description}`}
|
${completedTasksText || `- \`${task.id}\`: ${task.description}`}
|
||||||
|
|
||||||
Use \`background_output(task_id="<id>")\` to retrieve each result.
|
Use \`background_output(task_id="<id>")\` to retrieve each result.
|
||||||
</system-reminder>`
|
</system-reminder>`
|
||||||
@ -256,7 +258,10 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|||||||
log("[background-agent] Failed to send notification:", error)
|
log("[background-agent] Failed to send notification:", error)
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskId = task.id
|
if (allComplete) {
|
||||||
|
for (const completedTask of completedTasks) {
|
||||||
|
const taskId = completedTask.id
|
||||||
|
state.clearCompletionTimer(taskId)
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
state.completionTimers.delete(taskId)
|
state.completionTimers.delete(taskId)
|
||||||
if (state.tasks.has(taskId)) {
|
if (state.tasks.has(taskId)) {
|
||||||
@ -267,3 +272,5 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|||||||
}, TASK_CLEANUP_DELAY_MS)
|
}, TASK_CLEANUP_DELAY_MS)
|
||||||
state.setCompletionTimer(taskId, timer)
|
state.setCompletionTimer(taskId, timer)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user