fix: respect disabled_tools config in agent prompts (fixes #2742)

- Check disabled_tools for 'question' in tool-config-handler permission logic
- Strip Question tool code examples from Prometheus prompts when disabled
- Pass disabled_tools through prometheus agent config builder pipeline
- Add tests for disabled_tools question permission handling
This commit is contained in:
YeonGyu-Kim
2026-03-23 18:13:38 +09:00
parent d886ac701f
commit f5eaa648e9
6 changed files with 153 additions and 5 deletions
@@ -0,0 +1,42 @@
import { describe, it, expect } from "bun:test"
import { getPrometheusPrompt } from "./system-prompt"
describe("getPrometheusPrompt", () => {
describe("#given question tool is not disabled", () => {
describe("#when generating prompt", () => {
it("#then should include Question tool references", () => {
const prompt = getPrometheusPrompt(undefined, [])
expect(prompt).toContain("Question({")
})
})
})
describe("#given question tool is disabled via disabled_tools", () => {
describe("#when generating prompt", () => {
it("#then should strip Question tool code examples", () => {
const prompt = getPrometheusPrompt(undefined, ["question"])
expect(prompt).not.toContain("Question({")
})
})
describe("#when disabled_tools includes question among other tools", () => {
it("#then should strip Question tool code examples", () => {
const prompt = getPrometheusPrompt(undefined, ["todowrite", "question", "interactive_bash"])
expect(prompt).not.toContain("Question({")
})
})
})
describe("#given no disabled_tools provided", () => {
describe("#when generating prompt with undefined", () => {
it("#then should include Question tool references", () => {
const prompt = getPrometheusPrompt(undefined, undefined)
expect(prompt).toContain("Question({")
})
})
})
})
+22 -4
View File
@@ -52,16 +52,34 @@ export function getPrometheusPromptSource(model?: string): PrometheusPromptSourc
* Gemini models → Gemini-optimized prompt (aggressive tool-call enforcement, thinking checkpoints) * Gemini models → Gemini-optimized prompt (aggressive tool-call enforcement, thinking checkpoints)
* Default (Claude, etc.) → Claude-optimized prompt (modular sections) * Default (Claude, etc.) → Claude-optimized prompt (modular sections)
*/ */
export function getPrometheusPrompt(model?: string): string { export function getPrometheusPrompt(model?: string, disabledTools?: readonly string[]): string {
const source = getPrometheusPromptSource(model) const source = getPrometheusPromptSource(model)
const isQuestionDisabled = disabledTools?.includes("question") ?? false
let prompt: string
switch (source) { switch (source) {
case "gpt": case "gpt":
return getGptPrometheusPrompt() prompt = getGptPrometheusPrompt()
break
case "gemini": case "gemini":
return getGeminiPrometheusPrompt() prompt = getGeminiPrometheusPrompt()
break
case "default": case "default":
default: default:
return PROMETHEUS_SYSTEM_PROMPT prompt = PROMETHEUS_SYSTEM_PROMPT
} }
if (isQuestionDisabled) {
prompt = stripQuestionToolReferences(prompt)
}
return prompt
}
/**
* Removes Question tool usage examples from prompt text when question tool is disabled.
*/
function stripQuestionToolReferences(prompt: string): string {
// Remove Question({...}) code blocks (multi-line)
return prompt.replace(/```typescript\n\s*Question\(\{[\s\S]*?\}\)\s*\n```/g, "")
} }
@@ -186,6 +186,7 @@ export async function applyAgentConfig(params: {
pluginPrometheusOverride: prometheusOverride, pluginPrometheusOverride: prometheusOverride,
userCategories: params.pluginConfig.categories, userCategories: params.pluginConfig.categories,
currentModel, currentModel,
disabledTools: params.pluginConfig.disabled_tools,
}); });
} }
@@ -27,6 +27,7 @@ export async function buildPrometheusAgentConfig(params: {
pluginPrometheusOverride: PrometheusOverride | undefined; pluginPrometheusOverride: PrometheusOverride | undefined;
userCategories: Record<string, CategoryConfig> | undefined; userCategories: Record<string, CategoryConfig> | undefined;
currentModel: string | undefined; currentModel: string | undefined;
disabledTools?: readonly string[];
}): Promise<Record<string, unknown>> { }): Promise<Record<string, unknown>> {
const categoryConfig = params.pluginPrometheusOverride?.category const categoryConfig = params.pluginPrometheusOverride?.category
? resolveCategoryConfig(params.pluginPrometheusOverride.category, params.userCategories) ? resolveCategoryConfig(params.pluginPrometheusOverride.category, params.userCategories)
@@ -69,7 +70,7 @@ export async function buildPrometheusAgentConfig(params: {
...(resolvedModel ? { model: resolvedModel } : {}), ...(resolvedModel ? { model: resolvedModel } : {}),
...(variantToUse ? { variant: variantToUse } : {}), ...(variantToUse ? { variant: variantToUse } : {}),
mode: "all", mode: "all",
prompt: getPrometheusPrompt(resolvedModel), prompt: getPrometheusPrompt(resolvedModel, params.disabledTools),
permission: PROMETHEUS_PERMISSION, permission: PROMETHEUS_PERMISSION,
description: `${(params.configAgentPlan?.description as string) ?? "Plan agent"} (Prometheus - OhMyOpenCode)`, description: `${(params.configAgentPlan?.description as string) ?? "Plan agent"} (Prometheus - OhMyOpenCode)`,
color: (params.configAgentPlan?.color as string) ?? "#FF5722", color: (params.configAgentPlan?.color as string) ?? "#FF5722",
@@ -5,6 +5,7 @@ import type { OhMyOpenCodeConfig } from "../config"
function createParams(overrides: { function createParams(overrides: {
taskSystem?: boolean taskSystem?: boolean
agents?: string[] agents?: string[]
disabledTools?: string[]
}) { }) {
const agentResult: Record<string, { permission?: Record<string, unknown> }> = {} const agentResult: Record<string, { permission?: Record<string, unknown> }> = {}
for (const agent of overrides.agents ?? []) { for (const agent of overrides.agents ?? []) {
@@ -15,6 +16,7 @@ function createParams(overrides: {
config: { tools: {}, permission: {} } as Record<string, unknown>, config: { tools: {}, permission: {} } as Record<string, unknown>,
pluginConfig: { pluginConfig: {
experimental: { task_system: overrides.taskSystem ?? false }, experimental: { task_system: overrides.taskSystem ?? false },
disabled_tools: overrides.disabledTools,
} as OhMyOpenCodeConfig, } as OhMyOpenCodeConfig,
agentResult: agentResult as Record<string, unknown>, agentResult: agentResult as Record<string, unknown>,
} }
@@ -183,4 +185,86 @@ describe("applyToolConfig", () => {
}) })
}) })
}) })
describe("#given disabled_tools includes 'question'", () => {
let originalConfigContent: string | undefined
let originalCliRunMode: string | undefined
beforeEach(() => {
originalConfigContent = process.env.OPENCODE_CONFIG_CONTENT
originalCliRunMode = process.env.OPENCODE_CLI_RUN_MODE
delete process.env.OPENCODE_CONFIG_CONTENT
delete process.env.OPENCODE_CLI_RUN_MODE
})
afterEach(() => {
if (originalConfigContent === undefined) {
delete process.env.OPENCODE_CONFIG_CONTENT
} else {
process.env.OPENCODE_CONFIG_CONTENT = originalConfigContent
}
if (originalCliRunMode === undefined) {
delete process.env.OPENCODE_CLI_RUN_MODE
} else {
process.env.OPENCODE_CLI_RUN_MODE = originalCliRunMode
}
})
describe("#when question is in disabled_tools", () => {
it.each(["sisyphus", "hephaestus", "prometheus"])(
"#then should deny question for %s agent",
(agentName) => {
const params = createParams({
agents: [agentName],
disabledTools: ["question"],
})
applyToolConfig(params)
const agent = params.agentResult[agentName] as {
permission: Record<string, unknown>
}
expect(agent.permission.question).toBe("deny")
},
)
})
describe("#when question is in disabled_tools alongside other tools", () => {
it.each(["sisyphus", "hephaestus", "prometheus"])(
"#then should deny question for %s agent",
(agentName) => {
const params = createParams({
agents: [agentName],
disabledTools: ["todowrite", "question", "interactive_bash"],
})
applyToolConfig(params)
const agent = params.agentResult[agentName] as {
permission: Record<string, unknown>
}
expect(agent.permission.question).toBe("deny")
},
)
})
describe("#when disabled_tools does not include question", () => {
it.each(["sisyphus", "hephaestus", "prometheus"])(
"#then should allow question for %s agent",
(agentName) => {
const params = createParams({
agents: [agentName],
disabledTools: ["todowrite", "interactive_bash"],
})
applyToolConfig(params)
const agent = params.agentResult[agentName] as {
permission: Record<string, unknown>
}
expect(agent.permission.question).toBe("allow")
},
)
})
})
}) })
@@ -44,7 +44,9 @@ export function applyToolConfig(params: {
const isCliRunMode = process.env.OPENCODE_CLI_RUN_MODE === "true"; const isCliRunMode = process.env.OPENCODE_CLI_RUN_MODE === "true";
const configQuestionPermission = getConfigQuestionPermission(); const configQuestionPermission = getConfigQuestionPermission();
const isQuestionDisabledByPlugin = params.pluginConfig.disabled_tools?.includes("question") ?? false;
const questionPermission = const questionPermission =
isQuestionDisabledByPlugin ? "deny" :
configQuestionPermission === "deny" ? "deny" : configQuestionPermission === "deny" ? "deny" :
isCliRunMode ? "deny" : isCliRunMode ? "deny" :
"allow"; "allow";