import type { Tool, ToolInputSchema } from './types.js'; import type { ToolPolicy, ToolPolicyContext } from './policy.js'; export interface AnthropicToolDef { name: string; description: string; input_schema: ToolInputSchema; } export interface OpenAIToolDef { type: 'function'; function: { name: string; description: string; parameters: ToolInputSchema; }; } export class ToolRegistry { private tools: Map = new Map(); private _policy?: ToolPolicy; /** Sanitize a tool name for API compatibility (dots → underscores). */ static sanitizeToolName(name: string): string { return name.replace(/\./g, '_'); } register(tool: Tool): void { if (this.tools.has(tool.name)) { throw new Error(`Tool '${tool.name}' is already registered`); } this.tools.set(tool.name, tool); } unregister(name: string): boolean { return this.tools.delete(name); } /** Replace an existing tool with a new implementation. Throws if not registered. */ replace(tool: Tool): void { if (!this.tools.has(tool.name)) { throw new Error(`Tool '${tool.name}' is not registered — cannot replace`); } this.tools.set(tool.name, tool); } /** Create a shallow clone of this registry (new Map, same Tool objects + policy). */ clone(): ToolRegistry { const cloned = new ToolRegistry(); for (const tool of this.tools.values()) { cloned.register(tool); } if (this._policy) { cloned.setPolicy(this._policy); } return cloned; } get(name: string): Tool | undefined { return this.tools.get(name); } /** Resolve a tool by its API-sanitized name (underscores → dots fallback). */ getByApiName(name: string): Tool | undefined { return this.tools.get(name) ?? this.tools.get(name.replace(/_/g, '.')); } list(): Tool[] { return Array.from(this.tools.values()); } /** Set the tool policy for filtering. */ setPolicy(policy: ToolPolicy): void { this._policy = policy; } /** Get the tool policy (if set). */ getPolicy(): ToolPolicy | undefined { return this._policy; } /** Return tools filtered by the policy for a given context. */ filteredList(context?: ToolPolicyContext): Tool[] { if (!this._policy) {return this.list();} return this._policy.filterTools(this.list(), context); } toAnthropicFormat(): AnthropicToolDef[] { return this.list().map(t => ({ name: ToolRegistry.sanitizeToolName(t.name), description: t.description, input_schema: t.inputSchema, })); } /** Return Anthropic-format tools filtered by policy. */ filteredToAnthropicFormat(context?: ToolPolicyContext): AnthropicToolDef[] { return this.filteredList(context).map(t => ({ name: ToolRegistry.sanitizeToolName(t.name), description: t.description, input_schema: t.inputSchema, })); } toOpenAIFormat(): OpenAIToolDef[] { return this.list().map(t => ({ type: 'function' as const, function: { name: ToolRegistry.sanitizeToolName(t.name), description: t.description, parameters: t.inputSchema, }, })); } /** Return OpenAI-format tools filtered by policy. */ filteredToOpenAIFormat(context?: ToolPolicyContext): OpenAIToolDef[] { return this.filteredList(context).map(t => ({ type: 'function' as const, function: { name: ToolRegistry.sanitizeToolName(t.name), description: t.description, parameters: t.inputSchema, }, })); } }