import * as interfaces from './smartagent.interfaces.js'; import { BaseToolWrapper } from './smartagent.tools.base.js'; /** * ToolRegistry - Manages tool registration, visibility, and lifecycle * * Responsibilities: * - Track all registered tools with their metadata * - Manage visibility (initial vs on-demand) * - Handle activation of on-demand tools * - Provide search functionality */ export class ToolRegistry { private tools: Map = new Map(); private metadata: Map = new Map(); private activated: Set = new Set(); /** * Register a tool with optional visibility settings */ register(tool: BaseToolWrapper, options: interfaces.IToolRegistrationOptions = {}): void { const visibility = options.visibility ?? 'initial'; this.tools.set(tool.name, tool); this.metadata.set(tool.name, { name: tool.name, description: tool.description, actions: tool.actions, visibility, isActivated: visibility === 'initial', isInitialized: false, tags: options.tags, category: options.category, }); if (visibility === 'initial') { this.activated.add(tool.name); } } /** * Get tools visible to the Driver (initial + activated on-demand) */ getVisibleTools(): BaseToolWrapper[] { return Array.from(this.tools.entries()) .filter(([name]) => this.activated.has(name)) .map(([, tool]) => tool); } /** * Get all tools (for search results) */ getAllTools(): BaseToolWrapper[] { return Array.from(this.tools.values()); } /** * Get a specific tool by name */ getTool(name: string): BaseToolWrapper | undefined { return this.tools.get(name); } /** * Get metadata for a tool */ getMetadata(name: string): interfaces.IToolMetadata | undefined { return this.metadata.get(name); } /** * Get all metadata */ getAllMetadata(): interfaces.IToolMetadata[] { return Array.from(this.metadata.values()); } /** * Search tools by query (matches name, description, tags, action names) */ search(query: string): interfaces.IToolMetadata[] { const q = query.toLowerCase(); return this.getAllMetadata().filter((meta) => { if (meta.name.toLowerCase().includes(q)) return true; if (meta.description.toLowerCase().includes(q)) return true; if (meta.tags?.some((t) => t.toLowerCase().includes(q))) return true; if (meta.category?.toLowerCase().includes(q)) return true; if ( meta.actions.some( (a) => a.name.toLowerCase().includes(q) || a.description.toLowerCase().includes(q) ) ) return true; return false; }); } /** * Activate an on-demand tool */ async activate(name: string): Promise<{ success: boolean; error?: string }> { const tool = this.tools.get(name); const meta = this.metadata.get(name); if (!tool || !meta) { return { success: false, error: `Tool "${name}" not found` }; } if (this.activated.has(name)) { return { success: true }; // Already activated } // Initialize if not already initialized if (!meta.isInitialized) { await tool.initialize(); meta.isInitialized = true; } this.activated.add(name); meta.isActivated = true; return { success: true }; } /** * Check if a tool is activated */ isActivated(name: string): boolean { return this.activated.has(name); } /** * Initialize all initial (visible) tools */ async initializeVisibleTools(): Promise { const promises: Promise[] = []; for (const [name, tool] of this.tools) { const meta = this.metadata.get(name); if (meta && this.activated.has(name) && !meta.isInitialized) { promises.push( tool.initialize().then(() => { meta.isInitialized = true; }) ); } } await Promise.all(promises); } /** * Cleanup all initialized tools */ async cleanup(): Promise { const promises: Promise[] = []; for (const [name, tool] of this.tools) { const meta = this.metadata.get(name); if (meta?.isInitialized) { promises.push(tool.cleanup()); } } await Promise.all(promises); } /** * Check if a tool exists in the registry */ has(name: string): boolean { return this.tools.has(name); } /** * Get the number of registered tools */ get size(): number { return this.tools.size; } /** * Get the number of activated tools */ get activatedCount(): number { return this.activated.size; } }