fix(background-agent): preserve external concurrency keys

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Jeremy Gollehon
2026-01-14 22:40:16 -08:00
parent c1246f61d1
commit 4ac0fa7bb0
2 changed files with 181 additions and 115 deletions
+37 -5
View File
@@ -129,8 +129,10 @@ export class BackgroundManager {
parentAgent: input.parentAgent,
model: input.model,
concurrencyKey,
concurrencyGroup: concurrencyKey,
}
this.tasks.set(task.id, task)
this.startPolling()
@@ -189,8 +191,9 @@ export class BackgroundManager {
existingTask.completedAt = new Date()
if (existingTask.concurrencyKey) {
this.concurrencyManager.release(existingTask.concurrencyKey)
existingTask.concurrencyKey = undefined
existingTask.concurrencyKey = undefined
}
this.markForNotification(existingTask)
this.notifyParentSession(existingTask).catch(err => {
log("[background-agent] Failed to notify on error:", err)
@@ -250,6 +253,33 @@ export class BackgroundManager {
parentAgent?: string
concurrencyKey?: string
}): Promise<BackgroundTask> {
const existingTask = this.tasks.get(input.taskId)
if (existingTask) {
if (input.parentSessionID !== existingTask.parentSessionID) {
existingTask.parentSessionID = input.parentSessionID
}
if (input.parentAgent !== undefined) {
existingTask.parentAgent = input.parentAgent
}
if (!existingTask.concurrencyGroup) {
existingTask.concurrencyGroup = input.concurrencyKey ?? existingTask.agent
}
subagentSessions.add(existingTask.sessionID)
this.startPolling()
// Track for batched notifications (external tasks need tracking too)
const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set()
pending.add(existingTask.id)
this.pendingByParent.set(input.parentSessionID, pending)
log("[background-agent] External task already registered:", { taskId: existingTask.id, sessionID: existingTask.sessionID })
return existingTask
}
const concurrencyGroup = input.concurrencyKey ?? input.agent ?? "sisyphus_task"
// Acquire concurrency slot if a key is provided
if (input.concurrencyKey) {
await this.concurrencyManager.acquire(input.concurrencyKey)
@@ -271,12 +301,14 @@ export class BackgroundManager {
},
parentAgent: input.parentAgent,
concurrencyKey: input.concurrencyKey,
concurrencyGroup,
}
this.tasks.set(task.id, task)
subagentSessions.add(input.sessionID)
this.startPolling()
// Track for batched notifications (external tasks need tracking too)
const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set()
pending.add(task.id)
@@ -301,12 +333,12 @@ export class BackgroundManager {
return existingTask
}
// Re-acquire concurrency using the agent name as the key (same as launch()).
// Note: existingTask.concurrencyKey is cleared when tasks complete, so we
// derive the key from task.agent which persists through completion.
const concurrencyKey = existingTask.agent
// Re-acquire concurrency using the persisted concurrency group
const concurrencyKey = existingTask.concurrencyGroup ?? existingTask.agent
await this.concurrencyManager.acquire(concurrencyKey)
existingTask.concurrencyKey = concurrencyKey
existingTask.concurrencyGroup = concurrencyKey
existingTask.status = "running"
existingTask.completedAt = undefined