145 lines
4.3 KiB
TypeScript
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;
|
|
}
|
|
}
|