feat(hooks): add read-only warning injection for Prometheus task delegation

When Prometheus (Planner) spawns subagents via task tools (sisyphus_task, task, call_omo_agent), a system directive is injected into the prompt to ensure subagents understand they are in a planning consultation context and must NOT modify files.

🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
YeonGyu-Kim
2026-01-06 11:54:37 +09:00
parent f354724e64
commit 7567c40a81
3 changed files with 144 additions and 1 deletions

View File

@@ -7,3 +7,24 @@ export const ALLOWED_EXTENSIONS = [".md"]
export const ALLOWED_PATH_PREFIX = ".sisyphus/"
export const BLOCKED_TOOLS = ["Write", "Edit", "write", "edit"]
export const PLANNING_CONSULT_WARNING = `
---
[SYSTEM DIRECTIVE - READ-ONLY PLANNING CONSULTATION]
You are being invoked by Prometheus (Planner), a READ-ONLY planning agent.
**CRITICAL CONSTRAINTS:**
- DO NOT modify any files (no Write, Edit, or any file mutations)
- DO NOT execute commands that change system state
- DO NOT create, delete, or rename files
- ONLY provide analysis, recommendations, and information
**YOUR ROLE**: Provide consultation, research, and analysis to assist with planning.
Return your findings and recommendations. The actual implementation will be handled separately after planning is complete.
---
`

View File

@@ -159,4 +159,109 @@ describe("prometheus-md-only", () => {
hook["tool.execute.before"](input, output)
).resolves.toBeUndefined()
})
test("should inject read-only warning when Prometheus calls sisyphus_task", async () => {
// #given
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "sisyphus_task",
sessionID: "test-session",
callID: "call-1",
agent: "Prometheus (Planner)",
}
const output = {
args: { prompt: "Analyze this codebase" },
}
// #when
await hook["tool.execute.before"](input, output)
// #then
expect(output.args.prompt).toContain("[SYSTEM DIRECTIVE - READ-ONLY PLANNING CONSULTATION]")
expect(output.args.prompt).toContain("DO NOT modify any files")
})
test("should inject read-only warning when Prometheus calls task", async () => {
// #given
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "task",
sessionID: "test-session",
callID: "call-1",
agent: "Prometheus (Planner)",
}
const output = {
args: { prompt: "Research this library" },
}
// #when
await hook["tool.execute.before"](input, output)
// #then
expect(output.args.prompt).toContain("[SYSTEM DIRECTIVE - READ-ONLY PLANNING CONSULTATION]")
})
test("should inject read-only warning when Prometheus calls call_omo_agent", async () => {
// #given
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "call_omo_agent",
sessionID: "test-session",
callID: "call-1",
agent: "Prometheus (Planner)",
}
const output = {
args: { prompt: "Find implementation examples" },
}
// #when
await hook["tool.execute.before"](input, output)
// #then
expect(output.args.prompt).toContain("[SYSTEM DIRECTIVE - READ-ONLY PLANNING CONSULTATION]")
})
test("should not inject warning for non-Prometheus agents calling sisyphus_task", async () => {
// #given
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "sisyphus_task",
sessionID: "test-session",
callID: "call-1",
agent: "Sisyphus",
}
const originalPrompt = "Implement this feature"
const output = {
args: { prompt: originalPrompt },
}
// #when
await hook["tool.execute.before"](input, output)
// #then
expect(output.args.prompt).toBe(originalPrompt)
expect(output.args.prompt).not.toContain("[SYSTEM DIRECTIVE - READ-ONLY PLANNING CONSULTATION]")
})
test("should not double-inject warning if already present", async () => {
// #given
const hook = createPrometheusMdOnlyHook(createMockPluginInput())
const input = {
tool: "sisyphus_task",
sessionID: "test-session",
callID: "call-1",
agent: "Prometheus (Planner)",
}
const promptWithWarning = "Some prompt [SYSTEM DIRECTIVE - READ-ONLY PLANNING CONSULTATION] already here"
const output = {
args: { prompt: promptWithWarning },
}
// #when
await hook["tool.execute.before"](input, output)
// #then
const occurrences = (output.args.prompt as string).split("[SYSTEM DIRECTIVE - READ-ONLY PLANNING CONSULTATION]").length - 1
expect(occurrences).toBe(1)
})
})

View File

@@ -1,5 +1,5 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { HOOK_NAME, PROMETHEUS_AGENTS, ALLOWED_EXTENSIONS, ALLOWED_PATH_PREFIX, BLOCKED_TOOLS } from "./constants"
import { HOOK_NAME, PROMETHEUS_AGENTS, ALLOWED_EXTENSIONS, ALLOWED_PATH_PREFIX, BLOCKED_TOOLS, PLANNING_CONSULT_WARNING } from "./constants"
import { log } from "../../shared/logger"
export * from "./constants"
@@ -10,6 +10,8 @@ function isAllowedFile(filePath: string): boolean {
return hasAllowedExtension && isInAllowedPath
}
const TASK_TOOLS = ["sisyphus_task", "task", "call_omo_agent"]
export function createPrometheusMdOnlyHook(_ctx: PluginInput) {
return {
"tool.execute.before": async (
@@ -23,6 +25,21 @@ export function createPrometheusMdOnlyHook(_ctx: PluginInput) {
}
const toolName = input.tool
// Inject read-only warning for task tools called by Prometheus
if (TASK_TOOLS.includes(toolName)) {
const prompt = output.args.prompt as string | undefined
if (prompt && !prompt.includes("[SYSTEM DIRECTIVE - READ-ONLY PLANNING CONSULTATION]")) {
output.args.prompt = prompt + PLANNING_CONSULT_WARNING
log(`[${HOOK_NAME}] Injected read-only planning warning to ${toolName}`, {
sessionID: input.sessionID,
tool: toolName,
agent: agentName,
})
}
return
}
if (!BLOCKED_TOOLS.includes(toolName)) {
return
}