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:
@@ -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({")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -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";
|
||||||
|
|||||||
Reference in New Issue
Block a user