diff --git a/src/agents/prometheus/system-prompt.test.ts b/src/agents/prometheus/system-prompt.test.ts new file mode 100644 index 000000000..14c9c346f --- /dev/null +++ b/src/agents/prometheus/system-prompt.test.ts @@ -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({") + }) + }) + }) +}) diff --git a/src/agents/prometheus/system-prompt.ts b/src/agents/prometheus/system-prompt.ts index 4595d8ad0..768eb17c1 100644 --- a/src/agents/prometheus/system-prompt.ts +++ b/src/agents/prometheus/system-prompt.ts @@ -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, "") } diff --git a/src/plugin-handlers/agent-config-handler.ts b/src/plugin-handlers/agent-config-handler.ts index de316ffb7..33f15f233 100644 --- a/src/plugin-handlers/agent-config-handler.ts +++ b/src/plugin-handlers/agent-config-handler.ts @@ -186,6 +186,7 @@ export async function applyAgentConfig(params: { pluginPrometheusOverride: prometheusOverride, userCategories: params.pluginConfig.categories, currentModel, + disabledTools: params.pluginConfig.disabled_tools, }); } diff --git a/src/plugin-handlers/prometheus-agent-config-builder.ts b/src/plugin-handlers/prometheus-agent-config-builder.ts index 3c080ed10..61a33e74f 100644 --- a/src/plugin-handlers/prometheus-agent-config-builder.ts +++ b/src/plugin-handlers/prometheus-agent-config-builder.ts @@ -27,6 +27,7 @@ export async function buildPrometheusAgentConfig(params: { pluginPrometheusOverride: PrometheusOverride | undefined; userCategories: Record | undefined; currentModel: string | undefined; + disabledTools?: readonly string[]; }): Promise> { 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", diff --git a/src/plugin-handlers/tool-config-handler.test.ts b/src/plugin-handlers/tool-config-handler.test.ts index 0ef60d56f..b2c82049f 100644 --- a/src/plugin-handlers/tool-config-handler.test.ts +++ b/src/plugin-handlers/tool-config-handler.test.ts @@ -5,6 +5,7 @@ import type { OhMyOpenCodeConfig } from "../config" function createParams(overrides: { taskSystem?: boolean agents?: string[] + disabledTools?: string[] }) { const agentResult: Record }> = {} for (const agent of overrides.agents ?? []) { @@ -15,6 +16,7 @@ function createParams(overrides: { config: { tools: {}, permission: {} } as Record, pluginConfig: { experimental: { task_system: overrides.taskSystem ?? false }, + disabled_tools: overrides.disabledTools, } as OhMyOpenCodeConfig, agentResult: agentResult as Record, } @@ -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 + } + 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 + } + 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 + } + expect(agent.permission.question).toBe("allow") + }, + ) + }) + }) }) diff --git a/src/plugin-handlers/tool-config-handler.ts b/src/plugin-handlers/tool-config-handler.ts index a4486677c..4be1f2937 100644 --- a/src/plugin-handlers/tool-config-handler.ts +++ b/src/plugin-handlers/tool-config-handler.ts @@ -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";