feat: add tool allow/deny profiles with per-agent and per-provider filtering

Implements configurable tool filtering with four built-in profiles
(minimal, messaging, coding, full), global and per-agent/per-provider
allow/deny lists with glob pattern support, and defense-in-depth
enforcement at both tool listing and execution time.

New: src/tools/policy.ts (ToolPolicy engine), src/tools/policy.test.ts (37 tests)
Modified: config schema, tool registry, tool executor, NativeAgent,
AgentOrchestrator, daemon wiring, gateway tool handler, test mocks
This commit is contained in:
William Valentin
2026-02-06 15:30:34 -08:00
parent 8238d3e981
commit ee0af0cc06
13 changed files with 794 additions and 8 deletions
+15 -1
View File
@@ -1,6 +1,7 @@
import type { ToolResult } from './types.js';
import type { ToolRegistry } from './registry.js';
import type { HookEngine } from '../hooks/engine.js';
import type { ToolPolicyContext } from './policy.js';
export interface ToolExecutorConfig {
defaultTimeoutMs?: number;
@@ -20,12 +21,25 @@ export class ToolExecutor {
this.maxOutputBytes = config?.maxOutputBytes ?? 51_200;
}
async execute(toolName: string, args: unknown): Promise<ToolResult> {
async execute(toolName: string, args: unknown, context?: ToolPolicyContext): Promise<ToolResult> {
const tool = this.registry.get(toolName);
if (!tool) {
return { success: false, output: '', error: `Tool '${toolName}' not found` };
}
// Policy check (defense in depth — tools should also be filtered at listing time)
const policy = this.registry.getPolicy();
if (policy) {
const allNames = this.registry.list().map(t => t.name);
if (!policy.isAllowed(toolName, allNames, context)) {
return {
success: false,
output: '',
error: `Tool '${toolName}' is not allowed by tool policy`,
};
}
}
// Check hooks
const action = this.hooks.getAction(toolName);
if (action === 'confirm') {