From c173fa8f24fabed3e2997d75208400d1272566ac Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 10 Apr 2026 13:42:53 +0900 Subject: [PATCH] delegate-task: block prometheus task delegation --- src/plugin-handlers/tool-config-handler.test.ts | 17 +++++++++++++++++ src/plugin-handlers/tool-config-handler.ts | 6 +++--- src/tools/delegate-task/constants.ts | 6 +++--- .../delegate-task/sync-continuation.test.ts | 8 ++++---- src/tools/delegate-task/sync-continuation.ts | 4 ++-- src/tools/delegate-task/sync-prompt-sender.ts | 4 ++-- src/tools/delegate-task/tools.test.ts | 4 ++-- 7 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/plugin-handlers/tool-config-handler.test.ts b/src/plugin-handlers/tool-config-handler.test.ts index dd9e63fc6..1e832158a 100644 --- a/src/plugin-handlers/tool-config-handler.test.ts +++ b/src/plugin-handlers/tool-config-handler.test.ts @@ -267,6 +267,23 @@ describe("applyToolConfig", () => { }) }) + describe("#given prometheus agent permissions", () => { + describe("#when applying tool config", () => { + it("#then should deny task delegation tools for prometheus", () => { + const params = createParams({ agents: ["prometheus"] }) + + applyToolConfig(params) + + const agent = params.agentResult.prometheus as { + permission: Record + } + expect(agent.permission.task).toBe("deny") + expect(agent.permission["task_*"]).toBe("deny") + expect(agent.permission.teammate).toBe("deny") + }) + }) + }) + describe("#given disabled_tools includes 'question'", () => { let originalConfigContent: string | undefined let originalCliRunMode: string | undefined diff --git a/src/plugin-handlers/tool-config-handler.ts b/src/plugin-handlers/tool-config-handler.ts index dae34fda6..379d67f9c 100644 --- a/src/plugin-handlers/tool-config-handler.ts +++ b/src/plugin-handlers/tool-config-handler.ts @@ -105,10 +105,10 @@ export function applyToolConfig(params: { prometheus.permission = { ...prometheus.permission, call_omo_agent: "deny", - task: "allow", + task: "deny", question: questionPermission, - "task_*": "allow", - teammate: "allow", + "task_*": "deny", + teammate: "deny", ...denyTodoTools, }; } diff --git a/src/tools/delegate-task/constants.ts b/src/tools/delegate-task/constants.ts index bff305b13..b7457bcb1 100644 --- a/src/tools/delegate-task/constants.ts +++ b/src/tools/delegate-task/constants.ts @@ -329,13 +329,13 @@ export function isPlanAgent(agentName: string | undefined): boolean { } /** - * Plan family: plan + prometheus. Shares mutual delegation blocking and task tool permission. - * Does NOT share system prompt (only isPlanAgent controls that). + * Plan family: plan + prometheus. Shares mutual delegation blocking only. + * Does NOT share system prompt or task permission (only isPlanAgent controls those). */ export const PLAN_FAMILY_NAMES = ["plan", "prometheus"] /** - * Check if the given agent belongs to the plan family (blocking + task permission). + * Check if the given agent belongs to the plan family for mutual delegation blocking. */ export function isPlanFamily(category: string): boolean export function isPlanFamily(category: string | undefined): boolean diff --git a/src/tools/delegate-task/sync-continuation.test.ts b/src/tools/delegate-task/sync-continuation.test.ts index 104d7e84b..21bd6c55e 100644 --- a/src/tools/delegate-task/sync-continuation.test.ts +++ b/src/tools/delegate-task/sync-continuation.test.ts @@ -605,8 +605,8 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { }) }) - test("keeps plan-family task delegation available during sync continuation", async () => { - //#given - a resumed plan-family session should keep its intended task capability + test("keeps task delegation disabled during prometheus sync continuation", async () => { + //#given - a resumed prometheus session should stay unable to delegate tasks const promptAsyncCalls: Array<{ path: { id: string }; body: Record }> = [] const mockClient = { session: { @@ -656,7 +656,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { const args = { session_id: "ses_test_12345678", prompt: "continue planning", - description: "resume plan task", + description: "resume prometheus task", load_skills: [], run_in_background: false, } @@ -667,7 +667,7 @@ describe("executeSyncContinuation - toast cleanup error paths", () => { //#then expect(promptAsyncCalls).toHaveLength(1) expect(promptAsyncCalls[0]?.body.tools).toEqual({ - task: true, + task: false, call_omo_agent: true, question: false, }) diff --git a/src/tools/delegate-task/sync-continuation.ts b/src/tools/delegate-task/sync-continuation.ts index fa6f9f022..10bb09cdc 100644 --- a/src/tools/delegate-task/sync-continuation.ts +++ b/src/tools/delegate-task/sync-continuation.ts @@ -1,6 +1,6 @@ import type { DelegateTaskArgs, ToolContextWithMetadata } from "./types" import type { ExecutorContext, SessionMessage } from "./executor-types" -import { isPlanFamily } from "./constants" +import { isPlanAgent } from "./constants" import { storeToolMetadata } from "../../features/tool-metadata-store" import { resolveCallID } from "./resolve-call-id" import { getTaskToastManager } from "../../features/task-toast-manager" @@ -84,7 +84,7 @@ export async function executeSyncContinuation( storeToolMetadata(ctx.sessionID, callID, syncContMeta) } - const allowTask = isPlanFamily(resumeAgent) + const allowTask = isPlanAgent(resumeAgent) const tddEnabled = sisyphusAgentConfig?.tdd const effectivePrompt = buildTaskPrompt(args.prompt, resumeAgent, tddEnabled) const tools = { diff --git a/src/tools/delegate-task/sync-prompt-sender.ts b/src/tools/delegate-task/sync-prompt-sender.ts index bd38830e5..949ab4cc6 100644 --- a/src/tools/delegate-task/sync-prompt-sender.ts +++ b/src/tools/delegate-task/sync-prompt-sender.ts @@ -1,6 +1,6 @@ import type { DelegateTaskArgs, OpencodeClient, DelegatedModelConfig } from "./types" import type { SisyphusAgentConfig } from "../../config/schema" -import { isPlanFamily } from "./constants" +import { isPlanAgent } from "./constants" import { buildTaskPrompt } from "./prompt-builder" import { promptSyncWithModelSuggestionRetry, @@ -64,7 +64,7 @@ export async function sendSyncPrompt( }, deps: SendSyncPromptDeps = sendSyncPromptDeps ): Promise { - const allowTask = isPlanFamily(input.agentToUse) + const allowTask = isPlanAgent(input.agentToUse) const tddEnabled = input.sisyphusAgentConfig?.tdd const effectivePrompt = buildTaskPrompt(input.args.prompt, input.agentToUse, tddEnabled) const tools = { diff --git a/src/tools/delegate-task/tools.test.ts b/src/tools/delegate-task/tools.test.ts index 7c09f16ab..cda38592d 100644 --- a/src/tools/delegate-task/tools.test.ts +++ b/src/tools/delegate-task/tools.test.ts @@ -4105,7 +4105,7 @@ describe("sisyphus-task", () => { expect(promptBody.tools.task).toBe(true) }, { timeout: 20000 }) - test("prometheus subagent should have task permission (plan family)", async () => { + test("prometheus subagent should NOT have task permission", async () => { //#given const { createDelegateTask } = require("./tools") let promptBody: any @@ -4131,7 +4131,7 @@ describe("sisyphus-task", () => { ) //#then - expect(promptBody.tools.task).toBe(true) + expect(promptBody.tools.task).toBe(false) }, { timeout: 20000 }) test("non-plan subagent should NOT have task permission", async () => {