diff --git a/README.md b/README.md index 9ca3ba58..63754b61 100644 --- a/README.md +++ b/README.md @@ -111,19 +111,22 @@ DCP uses its own config file: > }, > // Distills key findings into preserved knowledge before removing raw content > "distill": { -> "enabled": true, +> // Permission mode: "allow" (no prompt), "ask" (prompt), "deny" (tool not registered) +> "permission": "allow", > // Show distillation content as an ignored message notification > "showDistillation": false, > }, > // Collapses a range of conversation content into a single summary > "compress": { -> "enabled": true, +> // Permission mode: "ask" (prompt), "allow" (no prompt), "deny" (tool not registered) +> "permission": "ask", > // Show summary content as an ignored message notification > "showCompression": false, > }, > // Removes tool content from context without preservation (for completed tasks or noise) > "prune": { -> "enabled": true, +> // Permission mode: "allow" (no prompt), "ask" (prompt), "deny" (tool not registered) +> "permission": "allow", > }, > }, > // Automatic pruning strategies diff --git a/dcp.schema.json b/dcp.schema.json index 32a1398f..82c85733 100644 --- a/dcp.schema.json +++ b/dcp.schema.json @@ -129,10 +129,11 @@ "description": "Configuration for the distill tool", "additionalProperties": false, "properties": { - "enabled": { - "type": "boolean", - "default": true, - "description": "Enable the distill tool" + "permission": { + "type": "string", + "enum": ["ask", "allow", "deny"], + "default": "allow", + "description": "Permission mode (deny disables the tool)" }, "showDistillation": { "type": "boolean", @@ -146,10 +147,11 @@ "description": "Configuration for the compress tool", "additionalProperties": false, "properties": { - "enabled": { - "type": "boolean", - "default": true, - "description": "Enable the compress tool" + "permission": { + "type": "string", + "enum": ["ask", "allow", "deny"], + "default": "ask", + "description": "Permission mode (deny disables the tool)" }, "showCompression": { "type": "boolean", @@ -163,10 +165,11 @@ "description": "Configuration for the prune tool", "additionalProperties": false, "properties": { - "enabled": { - "type": "boolean", - "default": true, - "description": "Enable the prune tool" + "permission": { + "type": "string", + "enum": ["ask", "allow", "deny"], + "default": "allow", + "description": "Permission mode (deny disables the tool)" } } } diff --git a/index.ts b/index.ts index 8aafd5c5..60c1b3ec 100644 --- a/index.ts +++ b/index.ts @@ -61,7 +61,7 @@ const plugin: Plugin = (async (ctx) => { ctx.directory, ), tool: { - ...(config.tools.distill.enabled && { + ...(config.tools.distill.permission !== "deny" && { distill: createDistillTool({ client: ctx.client, state, @@ -70,7 +70,7 @@ const plugin: Plugin = (async (ctx) => { workingDirectory: ctx.directory, }), }), - ...(config.tools.compress.enabled && { + ...(config.tools.compress.permission !== "deny" && { compress: createCompressTool({ client: ctx.client, state, @@ -79,7 +79,7 @@ const plugin: Plugin = (async (ctx) => { workingDirectory: ctx.directory, }), }), - ...(config.tools.prune.enabled && { + ...(config.tools.prune.permission !== "deny" && { prune: createPruneTool({ client: ctx.client, state, @@ -99,9 +99,9 @@ const plugin: Plugin = (async (ctx) => { } const toolsToAdd: string[] = [] - if (config.tools.distill.enabled) toolsToAdd.push("distill") - if (config.tools.compress.enabled) toolsToAdd.push("compress") - if (config.tools.prune.enabled) toolsToAdd.push("prune") + if (config.tools.distill.permission !== "deny") toolsToAdd.push("distill") + if (config.tools.compress.permission !== "deny") toolsToAdd.push("compress") + if (config.tools.prune.permission !== "deny") toolsToAdd.push("prune") if (toolsToAdd.length > 0) { const existingPrimaryTools = opencodeConfig.experimental?.primary_tools ?? [] @@ -112,18 +112,16 @@ const plugin: Plugin = (async (ctx) => { logger.info( `Added ${toolsToAdd.map((t) => `'${t}'`).join(" and ")} to experimental.primary_tools via config mutation`, ) - - // Set compress permission to ask (only if not already configured) - if (config.tools.compress.enabled) { - const permission = opencodeConfig.permission ?? {} - if (!("compress" in permission)) { - opencodeConfig.permission = { - ...permission, - compress: "ask", - } as typeof permission - } - } } + + // Set tool permissions from DCP config + const permission = opencodeConfig.permission ?? {} + opencodeConfig.permission = { + ...permission, + distill: config.tools.distill.permission, + compress: config.tools.compress.permission, + prune: config.tools.prune.permission, + } as typeof permission }, } }) satisfies Plugin diff --git a/lib/config.ts b/lib/config.ts index d75fd5b8..e2e6da61 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -10,16 +10,16 @@ export interface Deduplication { } export interface PruneTool { - enabled: boolean + permission: "ask" | "allow" | "deny" } export interface DistillTool { - enabled: boolean + permission: "ask" | "allow" | "deny" showDistillation: boolean } export interface CompressTool { - enabled: boolean + permission: "ask" | "allow" | "deny" showCompression: boolean } @@ -108,13 +108,13 @@ export const VALID_CONFIG_KEYS = new Set([ "tools.settings.protectedTools", "tools.settings.contextLimit", "tools.distill", - "tools.distill.enabled", + "tools.distill.permission", "tools.distill.showDistillation", "tools.compress", - "tools.compress.enabled", + "tools.compress.permission", "tools.compress.showCompression", "tools.prune", - "tools.prune.enabled", + "tools.prune.permission", "strategies", // strategies.deduplication "strategies.deduplication", @@ -303,12 +303,15 @@ function validateConfigTypes(config: Record): ValidationError[] { } } if (tools.distill) { - if (tools.distill.enabled !== undefined && typeof tools.distill.enabled !== "boolean") { - errors.push({ - key: "tools.distill.enabled", - expected: "boolean", - actual: typeof tools.distill.enabled, - }) + if (tools.distill.permission !== undefined) { + const validValues = ["ask", "allow", "deny"] + if (!validValues.includes(tools.distill.permission)) { + errors.push({ + key: "tools.distill.permission", + expected: '"ask" | "allow" | "deny"', + actual: JSON.stringify(tools.distill.permission), + }) + } } if ( tools.distill.showDistillation !== undefined && @@ -322,15 +325,15 @@ function validateConfigTypes(config: Record): ValidationError[] { } } if (tools.compress) { - if ( - tools.compress.enabled !== undefined && - typeof tools.compress.enabled !== "boolean" - ) { - errors.push({ - key: "tools.compress.enabled", - expected: "boolean", - actual: typeof tools.compress.enabled, - }) + if (tools.compress.permission !== undefined) { + const validValues = ["ask", "allow", "deny"] + if (!validValues.includes(tools.compress.permission)) { + errors.push({ + key: "tools.compress.permission", + expected: '"ask" | "allow" | "deny"', + actual: JSON.stringify(tools.compress.permission), + }) + } } if ( tools.compress.showCompression !== undefined && @@ -344,12 +347,15 @@ function validateConfigTypes(config: Record): ValidationError[] { } } if (tools.prune) { - if (tools.prune.enabled !== undefined && typeof tools.prune.enabled !== "boolean") { - errors.push({ - key: "tools.prune.enabled", - expected: "boolean", - actual: typeof tools.prune.enabled, - }) + if (tools.prune.permission !== undefined) { + const validValues = ["ask", "allow", "deny"] + if (!validValues.includes(tools.prune.permission)) { + errors.push({ + key: "tools.prune.permission", + expected: '"ask" | "allow" | "deny"', + actual: JSON.stringify(tools.prune.permission), + }) + } } } } @@ -499,15 +505,15 @@ const defaultConfig: PluginConfig = { contextLimit: 100000, }, distill: { - enabled: true, + permission: "allow", showDistillation: false, }, compress: { - enabled: true, + permission: "ask", showCompression: false, }, prune: { - enabled: true, + permission: "allow", }, }, strategies: { @@ -678,15 +684,15 @@ function mergeTools( contextLimit: override.settings?.contextLimit ?? base.settings.contextLimit, }, distill: { - enabled: override.distill?.enabled ?? base.distill.enabled, + permission: override.distill?.permission ?? base.distill.permission, showDistillation: override.distill?.showDistillation ?? base.distill.showDistillation, }, compress: { - enabled: override.compress?.enabled ?? base.compress.enabled, + permission: override.compress?.permission ?? base.compress.permission, showCompression: override.compress?.showCompression ?? base.compress.showCompression, }, prune: { - enabled: override.prune?.enabled ?? base.prune.enabled, + permission: override.prune?.permission ?? base.prune.permission, }, } } diff --git a/lib/hooks.ts b/lib/hooks.ts index 508b98f0..b3ecf7ff 100644 --- a/lib/hooks.ts +++ b/lib/hooks.ts @@ -43,9 +43,9 @@ export function createSystemPromptHandler( } const flags = { - prune: config.tools.prune.enabled, - distill: config.tools.distill.enabled, - compress: config.tools.compress.enabled, + prune: config.tools.prune.permission !== "deny", + distill: config.tools.distill.permission !== "deny", + compress: config.tools.compress.permission !== "deny", } if (!flags.prune && !flags.distill && !flags.compress) { diff --git a/lib/messages/inject.ts b/lib/messages/inject.ts index daa00e72..28d9854c 100644 --- a/lib/messages/inject.ts +++ b/lib/messages/inject.ts @@ -57,9 +57,9 @@ Context management was just performed. Do NOT use the ${toolName} again. A fresh const getNudgeString = (config: PluginConfig): string => { const flags = { - prune: config.tools.prune.enabled, - distill: config.tools.distill.enabled, - compress: config.tools.compress.enabled, + prune: config.tools.prune.permission !== "deny", + distill: config.tools.distill.permission !== "deny", + compress: config.tools.compress.permission !== "deny", } if (!flags.prune && !flags.distill && !flags.compress) { @@ -71,9 +71,9 @@ const getNudgeString = (config: PluginConfig): string => { const getCooldownMessage = (config: PluginConfig): string => { return wrapCooldownMessage({ - prune: config.tools.prune.enabled, - distill: config.tools.distill.enabled, - compress: config.tools.compress.enabled, + prune: config.tools.prune.permission !== "deny", + distill: config.tools.distill.permission !== "deny", + compress: config.tools.compress.permission !== "deny", }) } @@ -157,9 +157,9 @@ export const insertPruneToolContext = ( logger: Logger, messages: WithParts[], ): void => { - const pruneEnabled = config.tools.prune.enabled - const distillEnabled = config.tools.distill.enabled - const compressEnabled = config.tools.compress.enabled + const pruneEnabled = config.tools.prune.permission !== "deny" + const distillEnabled = config.tools.distill.permission !== "deny" + const compressEnabled = config.tools.compress.permission !== "deny" if (!pruneEnabled && !distillEnabled && !compressEnabled) { return