Files
smartagent/ts/smartagent.tools.expert.ts

145 lines
4.3 KiB
TypeScript

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;
}
}