189 lines
4.6 KiB
TypeScript
189 lines
4.6 KiB
TypeScript
|
|
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<string, BaseToolWrapper> = new Map();
|
||
|
|
private metadata: Map<string, interfaces.IToolMetadata> = new Map();
|
||
|
|
private activated: Set<string> = 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<void> {
|
||
|
|
const promises: Promise<void>[] = [];
|
||
|
|
|
||
|
|
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<void> {
|
||
|
|
const promises: Promise<void>[] = [];
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|