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:
parent
f354724e64
commit
7567c40a81
@ -7,3 +7,24 @@ export const ALLOWED_EXTENSIONS = [".md"]
|
|||||||
export const ALLOWED_PATH_PREFIX = ".sisyphus/"
|
export const ALLOWED_PATH_PREFIX = ".sisyphus/"
|
||||||
|
|
||||||
export const BLOCKED_TOOLS = ["Write", "Edit", "write", "edit"]
|
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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`
|
||||||
|
|||||||
@ -159,4 +159,109 @@ describe("prometheus-md-only", () => {
|
|||||||
hook["tool.execute.before"](input, output)
|
hook["tool.execute.before"](input, output)
|
||||||
).resolves.toBeUndefined()
|
).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)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
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"
|
import { log } from "../../shared/logger"
|
||||||
|
|
||||||
export * from "./constants"
|
export * from "./constants"
|
||||||
@ -10,6 +10,8 @@ function isAllowedFile(filePath: string): boolean {
|
|||||||
return hasAllowedExtension && isInAllowedPath
|
return hasAllowedExtension && isInAllowedPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TASK_TOOLS = ["sisyphus_task", "task", "call_omo_agent"]
|
||||||
|
|
||||||
export function createPrometheusMdOnlyHook(_ctx: PluginInput) {
|
export function createPrometheusMdOnlyHook(_ctx: PluginInput) {
|
||||||
return {
|
return {
|
||||||
"tool.execute.before": async (
|
"tool.execute.before": async (
|
||||||
@ -23,6 +25,21 @@ export function createPrometheusMdOnlyHook(_ctx: PluginInput) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const toolName = input.tool
|
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)) {
|
if (!BLOCKED_TOOLS.includes(toolName)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user