feat(tools): add ToolRegistry, ToolSearchTool and ExpertTool to support on-demand tool visibility, discovery, activation, and expert/subagent tooling; extend DualAgentOrchestrator API and interfaces to manage tool lifecycle

This commit is contained in:
2026-01-20 14:39:34 +00:00
parent 5ca0c80ea9
commit 5aa69cc998
10 changed files with 924 additions and 37 deletions

View File

@@ -8,6 +8,9 @@ import { HttpTool } from './smartagent.tools.http.js';
import { ShellTool } from './smartagent.tools.shell.js';
import { BrowserTool } from './smartagent.tools.browser.js';
import { DenoTool } from './smartagent.tools.deno.js';
import { ToolRegistry } from './smartagent.classes.toolregistry.js';
import { ToolSearchTool } from './smartagent.tools.search.js';
import { ExpertTool } from './smartagent.tools.expert.js';
/**
* DualAgentOrchestrator - Coordinates Driver and Guardian agents
@@ -20,7 +23,7 @@ export class DualAgentOrchestrator {
private guardianProvider: plugins.smartai.MultiModalModel;
private driver: DriverAgent;
private guardian: GuardianAgent;
private tools: Map<string, BaseToolWrapper> = new Map();
private registry: ToolRegistry = new ToolRegistry();
private isRunning = false;
private conversationHistory: interfaces.IAgentMessage[] = [];
private ownsSmartAi = true; // true if we created the SmartAi instance, false if it was provided
@@ -125,19 +128,55 @@ export class DualAgentOrchestrator {
}
/**
* Register a custom tool
* Register a custom tool with optional visibility settings
*/
public registerTool(tool: BaseToolWrapper): void {
this.tools.set(tool.name, tool);
// Register with agents if they exist (they're created in start())
if (this.driver) {
this.driver.registerTool(tool);
}
if (this.guardian) {
this.guardian.registerTool(tool);
public registerTool(
tool: BaseToolWrapper,
options?: interfaces.IToolRegistrationOptions
): void {
this.registry.register(tool, options);
// If initial visibility and agents exist, register with them
const visibility = options?.visibility ?? 'initial';
if (visibility === 'initial') {
if (this.driver) {
this.driver.registerTool(tool);
}
if (this.guardian) {
this.guardian.registerTool(tool);
}
}
}
/**
* Register an expert (subagent) as a tool
*/
public registerExpert(config: interfaces.IExpertConfig): void {
const expert = new ExpertTool(config, this.smartai);
this.registerTool(expert, {
visibility: config.visibility,
tags: config.tags,
category: config.category ?? 'expert',
});
}
/**
* Enable tool search functionality
* This adds a 'tools' tool that allows the Driver to discover and activate on-demand tools
*/
public enableToolSearch(): void {
const searchTool = new ToolSearchTool(this.registry, (tool) => {
// Callback when an on-demand tool is activated
if (this.driver) {
this.driver.registerTool(tool);
}
if (this.guardian) {
this.guardian.registerTool(tool);
}
});
this.registerTool(searchTool); // Always initial visibility
}
/**
* Register all standard tools
*/
@@ -193,19 +232,14 @@ export class DualAgentOrchestrator {
});
this.guardian = new GuardianAgent(this.guardianProvider, this.options.guardianPolicyPrompt);
// Register any tools that were added before start() with the agents
for (const tool of this.tools.values()) {
// Register visible tools with agents
for (const tool of this.registry.getVisibleTools()) {
this.driver.registerTool(tool);
this.guardian.registerTool(tool);
}
// Initialize all tools
const initPromises: Promise<void>[] = [];
for (const tool of this.tools.values()) {
initPromises.push(tool.initialize());
}
await Promise.all(initPromises);
// Initialize visible tools
await this.registry.initializeVisibleTools();
this.isRunning = true;
}
@@ -213,13 +247,7 @@ export class DualAgentOrchestrator {
* Cleanup all tools
*/
public async stop(): Promise<void> {
const cleanupPromises: Promise<void>[] = [];
for (const tool of this.tools.values()) {
cleanupPromises.push(tool.cleanup());
}
await Promise.all(cleanupPromises);
await this.registry.cleanup();
// Only stop smartai if we created it (don't stop external instances)
if (this.ownsSmartAi) {
@@ -432,7 +460,7 @@ Please output the exact XML format above.`
});
// Execute the tool
const tool = this.tools.get(proposal.toolName);
const tool = this.registry.getTool(proposal.toolName);
if (!tool) {
const errorMessage = `Tool "${proposal.toolName}" not found.`;
driverResponse = await this.driver.continueWithMessage(
@@ -652,6 +680,13 @@ Please output the exact XML format above.`
* Get registered tool names
*/
public getToolNames(): string[] {
return Array.from(this.tools.keys());
return this.registry.getAllMetadata().map((m) => m.name);
}
/**
* Get the tool registry for advanced operations
*/
public getRegistry(): ToolRegistry {
return this.registry;
}
}