feat: add allowlist denylist and preset list for tools
This commit is contained in:
parent
48fbec6659
commit
62f285a558
|
|
@ -146,7 +146,9 @@ export class Agent {
|
|||
maxTurns: this.config.maxTurns,
|
||||
maxTokens: this.config.maxTokens,
|
||||
temperature: this.config.temperature,
|
||||
toolPreset: this.config.toolPreset,
|
||||
allowedTools: this.config.tools,
|
||||
disallowedTools: this.config.disallowedTools,
|
||||
agentName: this.name,
|
||||
agentRole: this.config.systemPrompt?.slice(0, 50) ?? 'assistant',
|
||||
loopDetection: this.config.loopDetection,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import type {
|
|||
TraceEvent,
|
||||
LoopDetectionConfig,
|
||||
LoopDetectionInfo,
|
||||
LLMToolDef,
|
||||
} from '../types.js'
|
||||
import { TokenBudgetExceededError } from '../errors.js'
|
||||
import { LoopDetector } from './loop-detector.js'
|
||||
|
|
@ -35,6 +36,17 @@ import { emitTrace } from '../utils/trace.js'
|
|||
import type { ToolRegistry } from '../tool/framework.js'
|
||||
import type { ToolExecutor } from '../tool/executor.js'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tool presets
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Predefined tool sets for common agent use cases. */
|
||||
export const TOOL_PRESETS = {
|
||||
readonly: ['file_read', 'grep'],
|
||||
readwrite: ['file_read', 'file_write', 'file_edit', 'grep'],
|
||||
full: ['file_read', 'file_write', 'file_edit', 'grep', 'bash'],
|
||||
} as const satisfies Record<string, readonly string[]>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public interfaces
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -60,11 +72,15 @@ export interface RunnerOptions {
|
|||
/** AbortSignal that cancels any in-flight adapter call and stops the loop. */
|
||||
readonly abortSignal?: AbortSignal
|
||||
/**
|
||||
* Whitelist of tool names this runner is allowed to use.
|
||||
* When provided, only tools whose name appears in this list are sent to the
|
||||
* LLM. When omitted, all registered tools are available.
|
||||
* Tool access control configuration.
|
||||
* - `toolPreset`: Predefined tool sets for common use cases
|
||||
* - `allowedTools`: Whitelist of tool names (allowlist)
|
||||
* - `disallowedTools`: Blacklist of tool names (denylist)
|
||||
* Tools are resolved in order: preset → allowlist → denylist
|
||||
*/
|
||||
readonly toolPreset?: 'readonly' | 'readwrite' | 'full'
|
||||
readonly allowedTools?: readonly string[]
|
||||
readonly disallowedTools?: readonly string[]
|
||||
/** Display name of the agent driving this runner (used in tool context). */
|
||||
readonly agentName?: string
|
||||
/** Short role description of the agent (used in tool context). */
|
||||
|
|
@ -180,6 +196,52 @@ export class AgentRunner {
|
|||
this.maxTurns = options.maxTurns ?? 10
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tool resolution
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Resolve the final set of tools available to this agent based on the
|
||||
* three-layer configuration: preset → allowlist → denylist.
|
||||
*
|
||||
* Returns LLMToolDef[] for direct use with LLM adapters.
|
||||
*/
|
||||
private resolveTools(): LLMToolDef[] {
|
||||
// Validate configuration for contradictions
|
||||
if (this.options.allowedTools && this.options.disallowedTools) {
|
||||
const overlap = this.options.allowedTools.filter(tool =>
|
||||
this.options.disallowedTools!.includes(tool)
|
||||
)
|
||||
if (overlap.length > 0) {
|
||||
console.warn(
|
||||
`AgentRunner: tool "${overlap[0]}" appears in both allowedTools and disallowedTools. ` +
|
||||
'This is contradictory and may lead to unexpected behavior.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let tools = this.toolRegistry.toToolDefs()
|
||||
|
||||
// 1. Apply preset filter if set
|
||||
if (this.options.toolPreset) {
|
||||
const presetTools = new Set(TOOL_PRESETS[this.options.toolPreset] as readonly string[])
|
||||
tools = tools.filter(t => presetTools.has(t.name))
|
||||
}
|
||||
|
||||
// 2. Apply allowlist filter if set
|
||||
if (this.options.allowedTools) {
|
||||
tools = tools.filter(t => this.options.allowedTools!.includes(t.name))
|
||||
}
|
||||
|
||||
// 3. Apply denylist filter if set
|
||||
if (this.options.disallowedTools) {
|
||||
const denied = new Set(this.options.disallowedTools)
|
||||
tools = tools.filter(t => !denied.has(t.name))
|
||||
}
|
||||
|
||||
return tools
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public API
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
@ -241,12 +303,8 @@ export class AgentRunner {
|
|||
let budgetExceeded = false
|
||||
|
||||
// Build the stable LLM options once; model / tokens / temp don't change.
|
||||
// toToolDefs() returns LLMToolDef[] (inputSchema, camelCase) — matches
|
||||
// LLMChatOptions.tools from types.ts directly.
|
||||
const allDefs = this.toolRegistry.toToolDefs()
|
||||
const toolDefs = this.options.allowedTools
|
||||
? allDefs.filter(d => this.options.allowedTools!.includes(d.name))
|
||||
: allDefs
|
||||
// resolveTools() returns LLMToolDef[] with three-layer filtering applied.
|
||||
const toolDefs = this.resolveTools()
|
||||
|
||||
// Per-call abortSignal takes precedence over the static one.
|
||||
const effectiveAbortSignal = options.abortSignal ?? this.options.abortSignal
|
||||
|
|
|
|||
|
|
@ -207,6 +207,10 @@ export interface AgentConfig {
|
|||
readonly systemPrompt?: string
|
||||
/** Names of tools (from the tool registry) available to this agent. */
|
||||
readonly tools?: readonly string[]
|
||||
/** Names of tools explicitly disallowed for this agent. */
|
||||
readonly disallowedTools?: readonly string[]
|
||||
/** Predefined tool preset for common use cases. */
|
||||
readonly toolPreset?: 'readonly' | 'readwrite' | 'full'
|
||||
readonly maxTurns?: number
|
||||
readonly maxTokens?: number
|
||||
/** Maximum cumulative tokens (input + output) allowed for this run. */
|
||||
|
|
|
|||
Loading…
Reference in New Issue