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:
144
ts/smartagent.tools.expert.ts
Normal file
144
ts/smartagent.tools.expert.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as interfaces from './smartagent.interfaces.js';
|
||||
import { BaseToolWrapper } from './smartagent.tools.base.js';
|
||||
|
||||
// Forward declaration to avoid circular import at module load time
|
||||
// The actual import happens lazily in initialize()
|
||||
let DualAgentOrchestrator: typeof import('./smartagent.classes.dualagent.js').DualAgentOrchestrator;
|
||||
|
||||
/**
|
||||
* ExpertTool - A specialized agent wrapped as a tool
|
||||
*
|
||||
* Enables hierarchical agent architectures where the Driver can delegate
|
||||
* complex tasks to specialized experts with their own tools and policies.
|
||||
*/
|
||||
export class ExpertTool extends BaseToolWrapper {
|
||||
public name: string;
|
||||
public description: string;
|
||||
public actions: interfaces.IToolAction[] = [
|
||||
{
|
||||
name: 'consult',
|
||||
description: 'Delegate a task or question to this expert',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
task: { type: 'string', description: 'The task or question for the expert' },
|
||||
context: { type: 'string', description: 'Additional context to help the expert' },
|
||||
},
|
||||
required: ['task'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
private config: interfaces.IExpertConfig;
|
||||
private smartAi: plugins.smartai.SmartAi;
|
||||
private inner?: InstanceType<typeof DualAgentOrchestrator>;
|
||||
|
||||
constructor(config: interfaces.IExpertConfig, smartAi: plugins.smartai.SmartAi) {
|
||||
super();
|
||||
this.config = config;
|
||||
this.smartAi = smartAi;
|
||||
this.name = config.name;
|
||||
this.description = config.description;
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
// Lazy import to avoid circular dependency
|
||||
if (!DualAgentOrchestrator) {
|
||||
const module = await import('./smartagent.classes.dualagent.js');
|
||||
DualAgentOrchestrator = module.DualAgentOrchestrator;
|
||||
}
|
||||
|
||||
this.inner = new DualAgentOrchestrator({
|
||||
smartAiInstance: this.smartAi, // Share SmartAi instance
|
||||
defaultProvider: this.config.provider,
|
||||
driverSystemMessage: this.config.systemMessage,
|
||||
guardianPolicyPrompt: this.config.guardianPolicy,
|
||||
maxIterations: this.config.maxIterations ?? 10,
|
||||
});
|
||||
|
||||
// Register expert's tools
|
||||
if (this.config.tools) {
|
||||
for (const tool of this.config.tools) {
|
||||
// Tools in the config are IAgentToolWrapper, but we need BaseToolWrapper
|
||||
// Since all our tools extend BaseToolWrapper, this cast is safe
|
||||
this.inner.registerTool(tool as BaseToolWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
await this.inner.start();
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
async cleanup(): Promise<void> {
|
||||
if (this.inner) {
|
||||
await this.inner.stop();
|
||||
this.inner = undefined;
|
||||
}
|
||||
this.isInitialized = false;
|
||||
}
|
||||
|
||||
async execute(
|
||||
action: string,
|
||||
params: Record<string, unknown>
|
||||
): Promise<interfaces.IToolExecutionResult> {
|
||||
this.validateAction(action);
|
||||
this.ensureInitialized();
|
||||
|
||||
const task = params.task as string;
|
||||
const context = params.context as string | undefined;
|
||||
|
||||
const fullTask = context ? `Context: ${context}\n\nTask: ${task}` : task;
|
||||
|
||||
try {
|
||||
const result = await this.inner!.run(fullTask);
|
||||
|
||||
return {
|
||||
success: result.success,
|
||||
result: {
|
||||
response: result.result,
|
||||
iterations: result.iterations,
|
||||
status: result.status,
|
||||
},
|
||||
summary: result.success
|
||||
? `Expert "${this.name}" completed (${result.iterations} iterations)`
|
||||
: `Expert "${this.name}" failed: ${result.status}`,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: `Expert error: ${error instanceof Error ? error.message : String(error)}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
getCallSummary(action: string, params: Record<string, unknown>): string {
|
||||
const task = params.task as string;
|
||||
const preview = task.length > 60 ? task.substring(0, 60) + '...' : task;
|
||||
return `Consult ${this.name}: "${preview}"`;
|
||||
}
|
||||
|
||||
getToolExplanation(): string {
|
||||
return `## Expert: ${this.name}
|
||||
${this.description}
|
||||
|
||||
### Usage:
|
||||
Delegate tasks to this expert when you need specialized help.
|
||||
|
||||
\`\`\`
|
||||
<tool_call>
|
||||
<tool>${this.name}</tool>
|
||||
<action>consult</action>
|
||||
<params>{"task": "Your question or task", "context": "Optional background"}</params>
|
||||
</tool_call>
|
||||
\`\`\`
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the expert's configuration
|
||||
*/
|
||||
getConfig(): interfaces.IExpertConfig {
|
||||
return this.config;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user