refactor: migrate delegate_task to task tool with metadata fixes

- Rename delegate_task tool to task across codebase (100 files)
- Update model references: claude-opus-4-6 → 4-5, gpt-5.3-codex → 5.2-codex
- Add tool-metadata-store to restore metadata overwritten by fromPlugin()
- Add session ID polling for BackgroundManager task sessions
- Await async ctx.metadata() calls in tool executors
- Add ses_ prefix guard to getMessageDir for performance
- Harden BackgroundManager with idle deferral and error handling
- Fix duplicate task key in sisyphus-junior test object literals
- Fix unawaited showOutputToUser in ast_grep_replace
- Fix background=true → run_in_background=true in ultrawork prompt
- Fix duplicate task/task references in docs and comments
This commit is contained in:
YeonGyu-Kim
2026-02-06 16:01:54 +09:00
parent f1c794e63e
commit a691a3ac0a
78 changed files with 1182 additions and 403 deletions
+50 -6
View File
@@ -88,6 +88,7 @@ export class BackgroundManager {
private queuesByKey: Map<string, QueueItem[]> = new Map()
private processingKeys: Set<string> = new Set()
private completionTimers: Map<string, ReturnType<typeof setTimeout>> = new Map()
private idleDeferralTimers: Map<string, ReturnType<typeof setTimeout>> = new Map()
constructor(
ctx: PluginInput,
@@ -328,7 +329,6 @@ export class BackgroundManager {
tools: {
...getAgentToolRestrictions(input.agent),
task: false,
delegate_task: false,
call_omo_agent: true,
question: false,
},
@@ -357,6 +357,7 @@ export class BackgroundManager {
}).catch(() => {})
this.markForNotification(existingTask)
this.cleanupPendingByParent(existingTask)
this.notifyParentSession(existingTask).catch(err => {
log("[background-agent] Failed to notify on error:", err)
})
@@ -410,7 +411,7 @@ export class BackgroundManager {
}
/**
* Track a task created elsewhere (e.g., from delegate_task) for notification tracking.
* Track a task created elsewhere (e.g., from task) for notification tracking.
* This allows tasks created by other tools to receive the same toast/prompt notifications.
*/
async trackTask(input: {
@@ -458,7 +459,7 @@ export class BackgroundManager {
return existingTask
}
const concurrencyGroup = input.concurrencyKey ?? input.agent ?? "delegate_task"
const concurrencyGroup = input.concurrencyKey ?? input.agent ?? "task"
// Acquire concurrency slot if a key is provided
if (input.concurrencyKey) {
@@ -472,7 +473,7 @@ export class BackgroundManager {
parentMessageID: "",
description: input.description,
prompt: "",
agent: input.agent || "delegate_task",
agent: input.agent || "task",
status: "running",
startedAt: new Date(),
progress: {
@@ -587,7 +588,6 @@ export class BackgroundManager {
tools: {
...getAgentToolRestrictions(existingTask.agent),
task: false,
delegate_task: false,
call_omo_agent: true,
question: false,
},
@@ -614,6 +614,7 @@ export class BackgroundManager {
}
this.markForNotification(existingTask)
this.cleanupPendingByParent(existingTask)
this.notifyParentSession(existingTask).catch(err => {
log("[background-agent] Failed to notify on resume error:", err)
})
@@ -651,6 +652,13 @@ export class BackgroundManager {
const task = this.findBySession(sessionID)
if (!task) return
// Clear any pending idle deferral timer since the task is still active
const existingTimer = this.idleDeferralTimers.get(task.id)
if (existingTimer) {
clearTimeout(existingTimer)
this.idleDeferralTimers.delete(task.id)
}
if (partInfo?.type === "tool" || partInfo?.tool) {
if (!task.progress) {
task.progress = {
@@ -677,7 +685,17 @@ export class BackgroundManager {
// Edge guard: Require minimum elapsed time (5 seconds) before accepting idle
const elapsedMs = Date.now() - startedAt.getTime()
if (elapsedMs < MIN_IDLE_TIME_MS) {
log("[background-agent] Ignoring early session.idle, elapsed:", { elapsedMs, taskId: task.id })
const remainingMs = MIN_IDLE_TIME_MS - elapsedMs
if (!this.idleDeferralTimers.has(task.id)) {
log("[background-agent] Deferring early session.idle:", { elapsedMs, remainingMs, taskId: task.id })
const timer = setTimeout(() => {
this.idleDeferralTimers.delete(task.id)
this.handleEvent({ type: "session.idle", properties: { sessionID } })
}, remainingMs)
this.idleDeferralTimers.set(task.id, timer)
} else {
log("[background-agent] session.idle already deferred:", { elapsedMs, taskId: task.id })
}
return
}
@@ -736,6 +754,12 @@ export class BackgroundManager {
clearTimeout(existingTimer)
this.completionTimers.delete(task.id)
}
const idleTimer = this.idleDeferralTimers.get(task.id)
if (idleTimer) {
clearTimeout(idleTimer)
this.idleDeferralTimers.delete(task.id)
}
this.cleanupPendingByParent(task)
this.tasks.delete(task.id)
this.clearNotificationsForTask(task.id)
@@ -890,6 +914,12 @@ export class BackgroundManager {
this.completionTimers.delete(task.id)
}
const idleTimer = this.idleDeferralTimers.get(task.id)
if (idleTimer) {
clearTimeout(idleTimer)
this.idleDeferralTimers.delete(task.id)
}
this.cleanupPendingByParent(task)
if (abortSession && task.sessionID) {
@@ -1025,6 +1055,15 @@ export class BackgroundManager {
this.markForNotification(task)
// Ensure pending tracking is cleaned up even if notification fails
this.cleanupPendingByParent(task)
const idleTimer = this.idleDeferralTimers.get(task.id)
if (idleTimer) {
clearTimeout(idleTimer)
this.idleDeferralTimers.delete(task.id)
}
if (task.sessionID) {
this.client.session.abort({
path: { id: task.sessionID },
@@ -1511,6 +1550,11 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
}
this.completionTimers.clear()
for (const timer of this.idleDeferralTimers.values()) {
clearTimeout(timer)
}
this.idleDeferralTimers.clear()
this.concurrencyManager.clear()
this.tasks.clear()
this.notifications.clear()