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
+138 -12
View File
@@ -1,4 +1,5 @@
import { describe, test, expect, beforeEach, afterEach, spyOn } from "bun:test"
declare const require: (name: string) => any
const { describe, test, expect, beforeEach, afterEach, spyOn } = require("bun:test")
import { DEFAULT_CATEGORIES, CATEGORY_PROMPT_APPENDS, CATEGORY_DESCRIPTIONS, isPlanAgent, PLAN_AGENT_NAMES } from "./constants"
import { resolveCategoryConfig } from "./tools"
import type { CategoryConfig } from "../../config/schema"
@@ -207,6 +208,66 @@ describe("sisyphus-task", () => {
})
describe("category delegation config validation", () => {
test("fills subagent_type as sisyphus-junior when category is provided without subagent_type", async () => {
// given
const { createDelegateTask } = require("./tools")
const mockManager = {
launch: async () => ({
id: "task-123",
status: "pending",
description: "Test task",
agent: "sisyphus-junior",
sessionID: "test-session",
}),
}
const mockClient = {
app: { agents: async () => ({ data: [] }) },
config: { get: async () => ({}) },
provider: { list: async () => ({ data: { connected: ["openai"] } }) },
model: { list: async () => ({ data: [{ provider: "openai", id: "gpt-5.3-codex" }] }) },
session: {
create: async () => ({ data: { id: "test-session" } }),
prompt: async () => ({ data: {} }),
messages: async () => ({ data: [] }),
status: async () => ({ data: {} }),
},
}
const tool = createDelegateTask({
manager: mockManager,
client: mockClient,
})
const toolContext = {
sessionID: "parent-session",
messageID: "parent-message",
agent: "sisyphus",
abort: new AbortController().signal,
}
const args: {
description: string
prompt: string
category: string
run_in_background: boolean
load_skills: string[]
subagent_type?: string
} = {
description: "Quick category test",
prompt: "Do something",
category: "quick",
run_in_background: true,
load_skills: [],
}
// when
await tool.execute(args, toolContext)
// then
expect(args.subagent_type).toBe("sisyphus-junior")
}, { timeout: 10000 })
test("proceeds without error when systemDefaultModel is undefined", async () => {
// given a mock client with no model in config
const { createDelegateTask } = require("./tools")
@@ -304,6 +365,71 @@ describe("sisyphus-task", () => {
})
})
describe("background metadata sessionId", () => {
test("should wait for background sessionId and set metadata for TUI toolcall counting", async () => {
//#given - manager.launch returns before sessionID is available
const { createDelegateTask } = require("./tools")
const tasks = new Map<string, { id: string; sessionID?: string; status: string; description: string; agent: string }>()
const mockManager = {
getTask: (id: string) => tasks.get(id),
launch: async () => {
const task = { id: "bg_1", status: "pending", description: "Test task", agent: "explore" }
tasks.set(task.id, task)
setTimeout(() => {
tasks.set(task.id, { ...task, status: "running", sessionID: "ses_child" })
}, 20)
return task
},
}
const mockClient = {
app: { agents: async () => ({ data: [{ name: "explore", mode: "subagent" }] }) },
config: { get: async () => ({}) },
provider: { list: async () => ({ data: { connected: ["openai"] } }) },
model: { list: async () => ({ data: [{ provider: "openai", id: "gpt-5.3-codex" }] }) },
session: {
create: async () => ({ data: { id: "test-session" } }),
prompt: async () => ({ data: {} }),
messages: async () => ({ data: [] }),
status: async () => ({ data: {} }),
},
}
const tool = createDelegateTask({
manager: mockManager,
client: mockClient,
})
const metadataCalls: Array<{ title?: string; metadata?: Record<string, unknown> }> = []
const toolContext = {
sessionID: "parent-session",
messageID: "parent-message",
agent: "sisyphus",
abort: new AbortController().signal,
metadata: (input: { title?: string; metadata?: Record<string, unknown> }) => {
metadataCalls.push(input)
},
}
const args = {
description: "Explore task",
prompt: "Explore features directory deeply",
subagent_type: "explore",
run_in_background: true,
load_skills: [],
}
//#when
const result = await tool.execute(args, toolContext)
//#then - metadata should include sessionId (camelCase) once it's available
expect(String(result)).toContain("Background task launched")
const sessionIdCall = metadataCalls.find((c) => c.metadata?.sessionId === "ses_child")
expect(sessionIdCall).toBeDefined()
})
})
describe("resolveCategoryConfig", () => {
test("returns null for unknown category without user config", () => {
// given
@@ -1894,7 +2020,7 @@ describe("sisyphus-task", () => {
describe("browserProvider propagation", () => {
test("should resolve agent-browser skill when browserProvider is passed", async () => {
// given - delegate_task configured with browserProvider: "agent-browser"
// given - task configured with browserProvider: "agent-browser"
const { createDelegateTask } = require("./tools")
let promptBody: any
@@ -1949,7 +2075,7 @@ describe("sisyphus-task", () => {
}, { timeout: 20000 })
test("should NOT resolve agent-browser skill when browserProvider is not set", async () => {
// given - delegate_task without browserProvider (defaults to playwright)
// given - task without browserProvider (defaults to playwright)
const { createDelegateTask } = require("./tools")
const mockManager = { launch: async () => ({}) }
@@ -2720,8 +2846,8 @@ describe("sisyphus-task", () => {
}, { timeout: 20000 })
})
describe("prometheus subagent delegate_task permission", () => {
test("prometheus subagent should have delegate_task permission enabled", async () => {
describe("prometheus subagent task permission", () => {
test("prometheus subagent should have task permission enabled", async () => {
// given - sisyphus delegates to prometheus
const { createDelegateTask } = require("./tools")
let promptBody: any
@@ -2759,7 +2885,7 @@ describe("sisyphus-task", () => {
// when - sisyphus delegates to prometheus
await tool.execute(
{
description: "Test prometheus delegate_task permission",
description: "Test prometheus task permission",
prompt: "Create a plan",
subagent_type: "prometheus",
run_in_background: false,
@@ -2768,11 +2894,11 @@ describe("sisyphus-task", () => {
toolContext
)
// then - prometheus should have delegate_task permission
expect(promptBody.tools.delegate_task).toBe(true)
// then - prometheus should have task permission
expect(promptBody.tools.task).toBe(true)
}, { timeout: 20000 })
test("non-prometheus subagent should NOT have delegate_task permission", async () => {
test("non-prometheus subagent should NOT have task permission", async () => {
// given - sisyphus delegates to oracle (non-prometheus)
const { createDelegateTask } = require("./tools")
let promptBody: any
@@ -2810,7 +2936,7 @@ describe("sisyphus-task", () => {
// when - sisyphus delegates to oracle
await tool.execute(
{
description: "Test oracle no delegate_task permission",
description: "Test oracle no task permission",
prompt: "Consult on architecture",
subagent_type: "oracle",
run_in_background: false,
@@ -2819,8 +2945,8 @@ describe("sisyphus-task", () => {
toolContext
)
// then - oracle should NOT have delegate_task permission
expect(promptBody.tools.delegate_task).toBe(false)
// then - oracle should NOT have task permission
expect(promptBody.tools.task).toBe(false)
}, { timeout: 20000 })
})