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:
237
ts/smartagent.tools.search.ts
Normal file
237
ts/smartagent.tools.search.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
import * as interfaces from './smartagent.interfaces.js';
|
||||
import { BaseToolWrapper } from './smartagent.tools.base.js';
|
||||
import { ToolRegistry } from './smartagent.classes.toolregistry.js';
|
||||
|
||||
/**
|
||||
* ToolSearchTool - AI-facing interface for discovering and activating tools
|
||||
*
|
||||
* This tool enables the Driver to:
|
||||
* - Search for tools by capability
|
||||
* - List all available tools
|
||||
* - Activate on-demand tools
|
||||
* - Get detailed information about specific tools
|
||||
*/
|
||||
export class ToolSearchTool extends BaseToolWrapper {
|
||||
public name = 'tools';
|
||||
public description =
|
||||
'Search for and activate available tools and experts. Use this to discover specialized capabilities.';
|
||||
|
||||
public actions: interfaces.IToolAction[] = [
|
||||
{
|
||||
name: 'search',
|
||||
description: 'Search for tools by name, description, tags, or capabilities',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string', description: 'Search query' },
|
||||
},
|
||||
required: ['query'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'list',
|
||||
description: 'List all available tools grouped by visibility',
|
||||
parameters: { type: 'object', properties: {} },
|
||||
},
|
||||
{
|
||||
name: 'activate',
|
||||
description: 'Activate an on-demand tool to make it available for use',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Name of the tool to activate' },
|
||||
},
|
||||
required: ['name'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'details',
|
||||
description: 'Get detailed information about a specific tool',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', description: 'Name of the tool' },
|
||||
},
|
||||
required: ['name'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
private registry: ToolRegistry;
|
||||
private onToolActivated?: (tool: BaseToolWrapper) => void;
|
||||
|
||||
constructor(registry: ToolRegistry, onToolActivated?: (tool: BaseToolWrapper) => void) {
|
||||
super();
|
||||
this.registry = registry;
|
||||
this.onToolActivated = onToolActivated;
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
async cleanup(): Promise<void> {
|
||||
this.isInitialized = false;
|
||||
}
|
||||
|
||||
async execute(
|
||||
action: string,
|
||||
params: Record<string, unknown>
|
||||
): Promise<interfaces.IToolExecutionResult> {
|
||||
this.validateAction(action);
|
||||
|
||||
switch (action) {
|
||||
case 'search':
|
||||
return this.handleSearch(params.query as string);
|
||||
case 'list':
|
||||
return this.handleList();
|
||||
case 'activate':
|
||||
return this.handleActivate(params.name as string);
|
||||
case 'details':
|
||||
return this.handleDetails(params.name as string);
|
||||
default:
|
||||
return { success: false, error: `Unknown action: ${action}` };
|
||||
}
|
||||
}
|
||||
|
||||
private handleSearch(query: string): interfaces.IToolExecutionResult {
|
||||
const results = this.registry.search(query);
|
||||
return {
|
||||
success: true,
|
||||
result: results.map((m) => ({
|
||||
name: m.name,
|
||||
description: m.description,
|
||||
visibility: m.visibility,
|
||||
isActivated: m.isActivated,
|
||||
category: m.category,
|
||||
tags: m.tags,
|
||||
actionCount: m.actions.length,
|
||||
})),
|
||||
summary: `Found ${results.length} tools matching "${query}"`,
|
||||
};
|
||||
}
|
||||
|
||||
private handleList(): interfaces.IToolExecutionResult {
|
||||
const all = this.registry.getAllMetadata();
|
||||
const initial = all.filter((m) => m.visibility === 'initial');
|
||||
const onDemand = all.filter((m) => m.visibility === 'on-demand');
|
||||
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
initial: initial.map((m) => ({
|
||||
name: m.name,
|
||||
description: m.description,
|
||||
category: m.category,
|
||||
})),
|
||||
onDemand: onDemand.map((m) => ({
|
||||
name: m.name,
|
||||
description: m.description,
|
||||
category: m.category,
|
||||
isActivated: m.isActivated,
|
||||
})),
|
||||
summary: `${initial.length} initial, ${onDemand.length} on-demand`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private async handleActivate(name: string): Promise<interfaces.IToolExecutionResult> {
|
||||
const result = await this.registry.activate(name);
|
||||
|
||||
if (result.success && this.onToolActivated) {
|
||||
const tool = this.registry.getTool(name);
|
||||
if (tool) {
|
||||
this.onToolActivated(tool);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: result.success,
|
||||
result: result.success ? { name, message: `Tool "${name}" is now available` } : undefined,
|
||||
error: result.error,
|
||||
summary: result.success ? `Activated: ${name}` : result.error,
|
||||
};
|
||||
}
|
||||
|
||||
private handleDetails(name: string): interfaces.IToolExecutionResult {
|
||||
const tool = this.registry.getTool(name);
|
||||
const meta = this.registry.getMetadata(name);
|
||||
|
||||
if (!tool || !meta) {
|
||||
return { success: false, error: `Tool "${name}" not found` };
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
name: meta.name,
|
||||
description: meta.description,
|
||||
visibility: meta.visibility,
|
||||
isActivated: meta.isActivated,
|
||||
category: meta.category,
|
||||
tags: meta.tags,
|
||||
actions: meta.actions,
|
||||
fullExplanation: tool.getToolExplanation(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getCallSummary(action: string, params: Record<string, unknown>): string {
|
||||
switch (action) {
|
||||
case 'search':
|
||||
return `Search tools: "${params.query}"`;
|
||||
case 'list':
|
||||
return 'List all tools';
|
||||
case 'activate':
|
||||
return `Activate tool: ${params.name}`;
|
||||
case 'details':
|
||||
return `Get details: ${params.name}`;
|
||||
default:
|
||||
return `tools.${action}`;
|
||||
}
|
||||
}
|
||||
|
||||
getToolExplanation(): string {
|
||||
return `## Tool: tools
|
||||
Search for and manage available tools and experts.
|
||||
|
||||
### Actions:
|
||||
|
||||
**search** - Find tools by capability
|
||||
\`\`\`
|
||||
<tool_call>
|
||||
<tool>tools</tool>
|
||||
<action>search</action>
|
||||
<params>{"query": "database"}</params>
|
||||
</tool_call>
|
||||
\`\`\`
|
||||
|
||||
**list** - List all tools grouped by visibility
|
||||
\`\`\`
|
||||
<tool_call>
|
||||
<tool>tools</tool>
|
||||
<action>list</action>
|
||||
<params>{}</params>
|
||||
</tool_call>
|
||||
\`\`\`
|
||||
|
||||
**activate** - Activate an on-demand tool
|
||||
\`\`\`
|
||||
<tool_call>
|
||||
<tool>tools</tool>
|
||||
<action>activate</action>
|
||||
<params>{"name": "database_expert"}</params>
|
||||
</tool_call>
|
||||
\`\`\`
|
||||
|
||||
**details** - Get full information about a tool
|
||||
\`\`\`
|
||||
<tool_call>
|
||||
<tool>tools</tool>
|
||||
<action>details</action>
|
||||
<params>{"name": "filesystem"}</params>
|
||||
</tool_call>
|
||||
\`\`\`
|
||||
`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user