feat(coding-agent): add exclude tools option closes #5109

This commit is contained in:
Mario Zechner
2026-05-28 23:49:01 +02:00
parent 1ab2899800
commit 9380d5f2e4
12 changed files with 147 additions and 6 deletions

View File

@@ -28,6 +28,7 @@ export interface Args {
sessionDir?: string;
models?: string[];
tools?: string[];
excludeTools?: string[];
noTools?: boolean;
noBuiltinTools?: boolean;
extensions?: string[];
@@ -113,6 +114,11 @@ export function parseArgs(args: string[]): Args {
.split(",")
.map((s) => s.trim())
.filter((name) => name.length > 0);
} else if ((arg === "--exclude-tools" || arg === "-xt") && i + 1 < args.length) {
result.excludeTools = args[++i]
.split(",")
.map((s) => s.trim())
.filter((name) => name.length > 0);
} else if (arg === "--thinking" && i + 1 < args.length) {
const level = args[++i];
if (isValidThinkingLevel(level)) {
@@ -237,6 +243,8 @@ ${chalk.bold("Options:")}
--no-builtin-tools, -nbt Disable built-in tools by default but keep extension/custom tools enabled
--tools, -t <tools> Comma-separated allowlist of tool names to enable
Applies to built-in, extension, and custom tools
--exclude-tools, -xt <tools> Comma-separated denylist of tool names to disable
Applies to built-in, extension, and custom tools
--thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
--extension, -e <path> Load an extension file (can be used multiple times)
--no-extensions, -ne Disable extension discovery (explicit -e paths still work)
@@ -299,6 +307,9 @@ ${chalk.bold("Examples:")}
# Read-only mode (no file modifications possible)
${APP_NAME} --tools read,grep,find,ls -p "Review the code in src/"
# Disable one tool while keeping the rest available
${APP_NAME} --exclude-tools ask_question
# Export a session file to HTML
${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl
${APP_NAME} --export session.jsonl output.html

View File

@@ -54,6 +54,7 @@ export interface CreateAgentSessionFromServicesOptions {
thinkingLevel?: ThinkingLevel;
scopedModels?: Array<{ model: Model<any>; thinkingLevel?: ThinkingLevel }>;
tools?: string[];
excludeTools?: CreateAgentSessionOptions["excludeTools"];
noTools?: CreateAgentSessionOptions["noTools"];
customTools?: ToolDefinition[];
}
@@ -192,6 +193,7 @@ export async function createAgentSessionFromServices(
thinkingLevel: options.thinkingLevel,
scopedModels: options.scopedModels,
tools: options.tools,
excludeTools: options.excludeTools,
noTools: options.noTools,
customTools: options.customTools,
sessionStartEvent: options.sessionStartEvent,

View File

@@ -170,6 +170,8 @@ export interface AgentSessionConfig {
initialActiveToolNames?: string[];
/** Optional allowlist of tool names. When provided, only these tool names are exposed. */
allowedToolNames?: string[];
/** Optional denylist of tool names. When provided, these tool names are not exposed. */
excludedToolNames?: string[];
/**
* Override base tools (useful for custom runtimes).
*
@@ -294,6 +296,7 @@ export class AgentSession {
private _extensionRunnerRef?: { current?: ExtensionRunner };
private _initialActiveToolNames?: string[];
private _allowedToolNames?: Set<string>;
private _excludedToolNames?: Set<string>;
private _baseToolsOverride?: Record<string, AgentTool>;
private _sessionStartEvent: SessionStartEvent;
private _extensionUIContext?: ExtensionUIContext;
@@ -328,6 +331,7 @@ export class AgentSession {
this._extensionRunnerRef = config.extensionRunnerRef;
this._initialActiveToolNames = config.initialActiveToolNames;
this._allowedToolNames = config.allowedToolNames ? new Set(config.allowedToolNames) : undefined;
this._excludedToolNames = config.excludedToolNames ? new Set(config.excludedToolNames) : undefined;
this._baseToolsOverride = config.baseToolsOverride;
this._sessionStartEvent = config.sessionStartEvent ?? { type: "session_start", reason: "startup" };
@@ -2272,7 +2276,9 @@ export class AgentSession {
const previousRegistryNames = new Set(this._toolRegistry.keys());
const previousActiveToolNames = this.getActiveToolNames();
const allowedToolNames = this._allowedToolNames;
const isAllowedTool = (name: string): boolean => !allowedToolNames || allowedToolNames.has(name);
const excludedToolNames = this._excludedToolNames;
const isAllowedTool = (name: string): boolean =>
(!allowedToolNames || allowedToolNames.has(name)) && !excludedToolNames?.has(name);
const registeredTools = this._extensionRunner.getAllRegisteredTools();
const allCustomTools = [

View File

@@ -65,6 +65,8 @@ export interface CreateAgentSessionOptions {
* When provided, only the listed tool names are enabled.
*/
tools?: string[];
/** Optional denylist of tool names to disable. Applies after `tools` when both are provided. */
excludeTools?: string[];
/** Custom tools to register (in addition to built-in tools). */
customTools?: ToolDefinition[];
@@ -279,11 +281,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
const defaultActiveToolNames: ToolName[] = ["read", "bash", "edit", "write"];
const allowedToolNames = options.tools ?? (options.noTools === "all" ? [] : undefined);
const initialActiveToolNames: string[] = options.tools
? [...options.tools]
: options.noTools
? []
: defaultActiveToolNames;
const excludedToolNames = options.excludeTools;
const excludedToolNameSet = excludedToolNames ? new Set(excludedToolNames) : undefined;
const initialActiveToolNames: string[] = (
options.tools ? [...options.tools] : options.noTools ? [] : defaultActiveToolNames
).filter((name) => !excludedToolNameSet?.has(name));
let agent: Agent;
@@ -416,6 +418,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
modelRegistry,
initialActiveToolNames,
allowedToolNames,
excludedToolNames,
extensionRunnerRef,
sessionStartEvent: options.sessionStartEvent,
});

View File

@@ -425,6 +425,9 @@ function buildSessionOptions(
if (parsed.tools) {
options.tools = [...parsed.tools];
}
if (parsed.excludeTools) {
options.excludeTools = [...parsed.excludeTools];
}
return { options, cliThinkingFromModel, diagnostics };
}
@@ -646,6 +649,7 @@ export async function main(args: string[], options?: MainOptions) {
thinkingLevel: sessionOptions.thinkingLevel,
scopedModels: sessionOptions.scopedModels,
tools: sessionOptions.tools,
excludeTools: sessionOptions.excludeTools,
noTools: sessionOptions.noTools,
customTools: sessionOptions.customTools,
});