feat(background-agent): support cancelling pending tasks

This commit is contained in:
justsisyphus
2026-01-19 10:18:10 +09:00
parent 933c0c99c5
commit d6723a7d11
2 changed files with 71 additions and 13 deletions

View File

@@ -732,6 +732,42 @@ export class BackgroundManager {
}
}
/**
* Cancels a pending task by removing it from queue and marking as cancelled.
* Does NOT abort session (no session exists yet) or release concurrency slot (wasn't acquired).
*/
cancelPendingTask(taskId: string): boolean {
const task = this.tasks.get(taskId)
if (!task || task.status !== "pending") {
return false
}
// Find and remove from queue
const key = task.model
? `${task.model.providerID}/${task.model.modelID}`
: task.agent
const queue = this.queuesByKey.get(key)
if (queue) {
const index = queue.findIndex(item => item.task.id === taskId)
if (index !== -1) {
queue.splice(index, 1)
if (queue.length === 0) {
this.queuesByKey.delete(key)
}
}
}
// Mark as cancelled
task.status = "cancelled"
task.completedAt = new Date()
// Clean up pendingByParent
this.cleanupPendingByParent(task)
log("[background-agent] Cancelled pending task:", { taskId, key })
return true
}
private startPolling(): void {
if (this.pollingInterval) return

View File

@@ -383,24 +383,31 @@ export function createBackgroundCancel(manager: BackgroundManager, client: Openc
if (cancelAll) {
const tasks = manager.getAllDescendantTasks(toolContext.sessionID)
const runningTasks = tasks.filter(t => t.status === "running")
const cancellableTasks = tasks.filter(t => t.status === "running" || t.status === "pending")
if (runningTasks.length === 0) {
return `✅ No running background tasks to cancel.`
if (cancellableTasks.length === 0) {
return `✅ No running or pending background tasks to cancel.`
}
const results: string[] = []
for (const task of runningTasks) {
client.session.abort({
path: { id: task.sessionID },
}).catch(() => {})
for (const task of cancellableTasks) {
if (task.status === "pending") {
// Pending task: use manager method (no session to abort)
manager.cancelPendingTask(task.id)
results.push(`- ${task.id}: ${task.description} (pending)`)
} else {
// Running task: abort session
client.session.abort({
path: { id: task.sessionID },
}).catch(() => {})
task.status = "cancelled"
task.completedAt = new Date()
results.push(`- ${task.id}: ${task.description}`)
task.status = "cancelled"
task.completedAt = new Date()
results.push(`- ${task.id}: ${task.description} (running)`)
}
}
return `✅ Cancelled ${runningTasks.length} background task(s):
return `✅ Cancelled ${cancellableTasks.length} background task(s):
${results.join("\n")}`
}
@@ -410,11 +417,26 @@ ${results.join("\n")}`
return `❌ Task not found: ${args.taskId}`
}
if (task.status !== "running") {
if (task.status !== "running" && task.status !== "pending") {
return `❌ Cannot cancel task: current status is "${task.status}".
Only running tasks can be cancelled.`
Only running or pending tasks can be cancelled.`
}
if (task.status === "pending") {
// Pending task: use manager method (no session to abort, no slot to release)
const cancelled = manager.cancelPendingTask(task.id)
if (!cancelled) {
return `❌ Failed to cancel pending task: ${task.id}`
}
return `✅ Pending task cancelled successfully
Task ID: ${task.id}
Description: ${task.description}
Status: ${task.status}`
}
// Running task: abort session
// Fire-and-forget: abort 요청을 보내고 await 하지 않음
// await 하면 메인 세션까지 abort 되는 문제 발생
client.session.abort({