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

View File

@@ -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({")
})
})
})
})

View File

@@ -52,16 +52,34 @@ export function getPrometheusPromptSource(model?: string): PrometheusPromptSourc
* Gemini models → Gemini-optimized prompt (aggressive tool-call enforcement, thinking checkpoints)
* 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 isQuestionDisabled = disabledTools?.includes("question") ?? false
let prompt: string
switch (source) {
case "gpt":
return getGptPrometheusPrompt()
prompt = getGptPrometheusPrompt()
break
case "gemini":
return getGeminiPrometheusPrompt()
prompt = getGeminiPrometheusPrompt()
break
case "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, "")
}

View File

@@ -186,6 +186,7 @@ export async function applyAgentConfig(params: {
pluginPrometheusOverride: prometheusOverride,
userCategories: params.pluginConfig.categories,
currentModel,
disabledTools: params.pluginConfig.disabled_tools,
});
}

View File

@@ -27,6 +27,7 @@ export async function buildPrometheusAgentConfig(params: {
pluginPrometheusOverride: PrometheusOverride | undefined;
userCategories: Record<string, CategoryConfig> | undefined;
currentModel: string | undefined;
disabledTools?: readonly string[];
}): Promise<Record<string, unknown>> {
const categoryConfig = params.pluginPrometheusOverride?.category
? resolveCategoryConfig(params.pluginPrometheusOverride.category, params.userCategories)
@@ -69,7 +70,7 @@ export async function buildPrometheusAgentConfig(params: {
...(resolvedModel ? { model: resolvedModel } : {}),
...(variantToUse ? { variant: variantToUse } : {}),
mode: "all",
prompt: getPrometheusPrompt(resolvedModel),
prompt: getPrometheusPrompt(resolvedModel, params.disabledTools),
permission: PROMETHEUS_PERMISSION,
description: `${(params.configAgentPlan?.description as string) ?? "Plan agent"} (Prometheus - OhMyOpenCode)`,
color: (params.configAgentPlan?.color as string) ?? "#FF5722",

View File

@@ -5,6 +5,7 @@ import type { OhMyOpenCodeConfig } from "../config"
function createParams(overrides: {
taskSystem?: boolean
agents?: string[]
disabledTools?: string[]
}) {
const agentResult: Record<string, { permission?: Record<string, unknown> }> = {}
for (const agent of overrides.agents ?? []) {
@@ -15,6 +16,7 @@ function createParams(overrides: {
config: { tools: {}, permission: {} } as Record<string, unknown>,
pluginConfig: {
experimental: { task_system: overrides.taskSystem ?? false },
disabled_tools: overrides.disabledTools,
} as OhMyOpenCodeConfig,
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")
},
)
})
})
})

View File

@@ -44,7 +44,9 @@ export function applyToolConfig(params: {
const isCliRunMode = process.env.OPENCODE_CLI_RUN_MODE === "true";
const configQuestionPermission = getConfigQuestionPermission();
const isQuestionDisabledByPlugin = params.pluginConfig.disabled_tools?.includes("question") ?? false;
const questionPermission =
isQuestionDisabledByPlugin ? "deny" :
configQuestionPermission === "deny" ? "deny" :
isCliRunMode ? "deny" :
"allow";