executor.tsā¢18.6 kB
import { WorksonaAgent, AgentExecutionContext, AgentExecutionResult } from './types';
import { ContextManager } from '../utils/context';
export class AgentExecutor {
private executionCount = 0;
private executionTimes: number[] = [];
constructor(private contextManager: ContextManager) {}
async executeAgent(
agent: WorksonaAgent,
request: string,
context: AgentExecutionContext
): Promise<AgentExecutionResult> {
const startTime = Date.now();
this.executionCount++;
console.error(`šÆ Executing agent: ${agent.name}`);
console.error(`š Request: ${request.substring(0, 100)}...`);
try {
// Build comprehensive execution prompt
const prompt = this.buildExecutionPrompt(agent, request, context);
// Simulate agent execution (in a real implementation, this would call Claude API)
const response = await this.callAgent(prompt, agent, context);
// Calculate execution time
const executionTime = Date.now() - startTime;
this.executionTimes.push(executionTime);
// Update memory with execution results
await this.updateAgentMemory(agent, request, response, context);
const result: AgentExecutionResult = {
agentName: agent.name,
request,
response,
metadata: {
executionTime,
toolsUsed: this.extractToolsUsed(response),
suggestedNextAgents: this.suggestFollowupAgents(agent, response),
confidence: this.calculateConfidence(agent, request, response),
tokens: this.estimateTokens(prompt + response)
}
};
console.error(`ā
Agent ${agent.name} completed in ${executionTime}ms`);
return result;
} catch (error) {
console.error(`ā Agent ${agent.name} failed:`, error);
const executionTime = Date.now() - startTime;
return {
agentName: agent.name,
request,
response: `Error executing agent: ${error instanceof Error ? error.message : String(error)}`,
metadata: {
executionTime,
toolsUsed: [],
suggestedNextAgents: [],
confidence: 0
}
};
}
}
private buildExecutionPrompt(
agent: WorksonaAgent,
request: string,
context: AgentExecutionContext
): string {
let prompt = agent.prompt;
// Add project context if available
if (context.projectContext) {
prompt += `\\n\\n## Current Project Context\\n`;
prompt += `**Project**: ${context.projectContext.name}\\n`;
prompt += `**Type**: ${context.projectContext.type}\\n`;
prompt += `**Tech Stack**: ${JSON.stringify(context.projectContext.techStack, null, 2)}\\n`;
prompt += `**Current Phase**: ${context.projectContext.currentPhase}\\n`;
const goals = context.projectContext.goals || [];
prompt += `**Goals**: ${goals.join(', ')}\\n`;
if (Object.keys(context.projectContext.standards).length > 0) {
prompt += `**Standards**: ${JSON.stringify(context.projectContext.standards, null, 2)}\\n`;
}
}
// Add conversation history context
if (context.conversationHistory?.length) {
prompt += `\\n\\n## Recent Conversation Context\\n`;
context.conversationHistory.slice(-3).forEach((turn, i) => {
prompt += `**Turn ${i + 1}**: ${turn.user}\\n`;
prompt += `**Response**: ${turn.assistant.substring(0, 200)}...\\n\\n`;
});
}
// Add relevant memory context
const relevantMemory = context.memory.getRelevantMemory(agent.name, request);
if (relevantMemory.length) {
prompt += `\\n\\n## Relevant Memory Context\\n`;
relevantMemory.forEach(memory => {
const summary = memory.summary || memory.response.substring(0, 150) + '...';
prompt += `- **${memory.agentName}** (${memory.timestamp.toLocaleDateString()}): ${summary}\\n`;
});
}
// Add agent capabilities reminder
prompt += `\\n\\n## Your Capabilities\\n`;
// Safe capability access with fallbacks
const primaryCaps = agent.capabilities?.primary || [];
const secondaryCaps = agent.capabilities?.secondary || [];
const frameworkCaps = agent.capabilities?.frameworks || [];
prompt += `**Primary**: ${primaryCaps.join(', ')}\\n`;
if (secondaryCaps.length) {
prompt += `**Secondary**: ${secondaryCaps.join(', ')}\\n`;
}
if (frameworkCaps.length) {
prompt += `**Frameworks**: ${frameworkCaps.join(', ')}\\n`;
}
// Add current request
prompt += `\\n\\n## Current Request\\n${request}`;
// Add available tools
const availableTools = context.availableTools || [];
prompt += `\\n\\n## Available Tools\\n${availableTools.join(', ')}`;
// Add quality expectations
if (agent.quality?.enterpriseGrade) {
prompt += `\\n\\n## Quality Standards\\n`;
prompt += `- Provide enterprise-grade, production-ready solutions\\n`;
prompt += `- Follow industry best practices and standards\\n`;
prompt += `- Include comprehensive documentation and examples\\n`;
if (agent.quality.professionalLevel) {
prompt += `- Apply ${agent.quality.professionalLevel}-level expertise\\n`;
}
}
// Add coordination context
if (agent.coordination?.worksWellWith?.length) {
prompt += `\\n\\n## Coordination Notes\\n`;
prompt += `Consider how your work might integrate with: ${agent.coordination.worksWellWith.join(', ')}\\n`;
}
return prompt;
}
private async callAgent(
prompt: string,
agent: WorksonaAgent,
context: AgentExecutionContext
): Promise<string> {
// When running inside Claude Desktop (MCP environment), we should return the agent prompt
// and let Claude Desktop handle the AI processing instead of making separate API calls
const isClaudeDesktop = this.isRunningInClaudeDesktop();
if (isClaudeDesktop) {
console.error('š¤ Running inside Claude Desktop - using integrated AI processing');
// Extract the actual request from the full prompt
const requestMatch = prompt.match(/## Current Request\n(.*?)(?:\n\n##|$)/s);
const actualRequest = requestMatch ? requestMatch[1].trim() : prompt;
// Add explicit code generation instructions
const codeInstructions = `
## CRITICAL INSTRUCTIONS - CODE GENERATION REQUIRED
You MUST provide working, complete, production-ready code as SEPARATE FILES. Do NOT just provide documentation, architecture, or explanations.
**REQUIREMENTS:**
1. **Generate actual code files** - Provide complete, runnable code
2. **Create separate, distinct files** - Each file should be clearly separated
3. **Include all necessary imports and dependencies**
4. **Provide working examples** - Code that can be immediately used
5. **Be specific and concrete** - No placeholders like [Your code here] or [Implementation details]
6. **Add proper error handling** - Include try/catch blocks and validation
7. **Use modern best practices** - Latest syntax, patterns, and approaches
8. **Make it production-ready** - Code should be enterprise-grade and deployable
**MANDATORY FILE FORMAT:**
Use this EXACT format for each file:
\`\`\`
## FILE: path/to/filename.ext
\`\`\`language
[Complete file contents here]
\`\`\`
**EXAMPLE:**
\`\`\`
## FILE: src/components/Button.tsx
\`\`\`typescript
import React from 'react';
interface ButtonProps {
children: React.ReactNode;
onClick?: () => void;
}
export const Button: React.FC<ButtonProps> = ({ children, onClick }) => {
return (
<button onClick={onClick} className="btn">
{children}
</button>
);
};
\`\`\`
## FILE: src/styles/Button.css
\`\`\`css
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
background: #007bff;
color: white;
cursor: pointer;
}
\`\`\`
\`\`\`
**OUTPUT STRUCTURE:**
1. Brief summary (1-2 sentences)
2. Multiple separate files using the FILE format above
3. Setup/installation instructions
4. Usage examples
**AVOID:**
- Single large code blocks without file separation
- Generic explanations without code
- Architecture discussions without implementation
- Placeholder code or pseudo-code
- Collaborative document format
Remember: Create SEPARATE FILES that can be copied individually, not a single collaborative document.`;
// Return the agent prompt with enhanced instructions and the request
return `${agent.prompt}${codeInstructions}\n\n## Current Request\n${actualRequest}`;
}
// For standalone usage, check for API key
const apiKey = process.env.ANTHROPIC_API_KEY;
if (!apiKey) {
console.error('ā ļø ANTHROPIC_API_KEY not found - using fallback response');
return this.generateFallbackResponse(agent, prompt, context, 'ANTHROPIC_API_KEY not found');
}
console.error('š Making external API call to Claude...');
try {
// Check if fetch is available
if (typeof fetch === 'undefined') {
console.error('ā fetch is not available in this Node.js environment');
return this.generateFallbackResponse(agent, prompt, context, 'fetch not available');
}
// Make actual API call to Claude
console.error('š” Sending request to Anthropic API...');
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
'anthropic-version': '2023-06-01'
},
body: JSON.stringify({
model: 'claude-3-sonnet-20240229',
max_tokens: 4000,
messages: [{
role: 'user',
content: prompt
}],
temperature: 0.7
}),
signal: controller.signal
});
clearTimeout(timeoutId);
console.error(`š” API Response status: ${response.status}`);
if (!response.ok) {
const errorText = await response.text();
console.error(`ā Claude API error (${response.status}): ${errorText}`);
return this.generateFallbackResponse(agent, prompt, context, `API Error: ${response.status}`);
}
const data = await response.json() as any;
if (!data.content || !Array.isArray(data.content) || !data.content[0] || !data.content[0].text) {
console.error('ā Invalid Claude API response structure');
return this.generateFallbackResponse(agent, prompt, context, 'Invalid API response');
}
// Return the actual AI-generated response
return data.content[0].text;
} catch (error) {
console.error('ā Claude API call failed:', error);
if (error instanceof Error) {
if (error.name === 'AbortError') {
console.error('ā° API call timed out after 30 seconds');
return this.generateFallbackResponse(agent, prompt, context, 'API timeout after 30 seconds');
}
return this.generateFallbackResponse(agent, prompt, context, error.message);
}
return this.generateFallbackResponse(agent, prompt, context, 'Unknown error');
}
}
private isRunningInClaudeDesktop(): boolean {
// Detect if we're running inside Claude Desktop MCP environment
return (
// Check if stdin/stdout are pipes (typical for MCP servers)
(!process.stdin.isTTY && !process.stdout.isTTY) ||
// Check for Claude Desktop specific process arguments or environment
process.argv.some(arg => arg.includes('claude') || arg.includes('mcp')) ||
// Check if we're being called as a subprocess without a terminal
process.env.NODE_ENV !== 'development' && !process.env.ANTHROPIC_API_KEY
);
}
private generateFallbackResponse(
agent: WorksonaAgent,
prompt: string,
context: AgentExecutionContext,
errorReason?: string
): string {
const requestSummary = prompt.split('## Current Request\\n')[1]?.split('\\n')[0] || 'Unknown request';
let response = `# šÆ ${agent.name} Agent Response\\n\\n`;
if (errorReason) {
response += `ā ļø *Note: Using fallback response due to: ${errorReason}*\\n\\n`;
}
response += `I understand you need: "${requestSummary}"\\n\\n`;
// Safe capability access with fallback
const primaryCapabilities = agent.capabilities?.primary || [];
const capabilitiesText = primaryCapabilities.length > 0
? primaryCapabilities.join(', ')
: 'general problem-solving';
response += `Based on my expertise in **${capabilitiesText}**, I would provide working code for: "${requestSummary}"\\n\\n`;
response += `ā ļø **Note**: This is a fallback response. With a valid API key, I would generate:\\n\\n`;
// Add agent-specific response structure focused on code output
if (agent.category === 'software-engineering') {
response += `## Complete Code Implementation\\n`;
response += `- Full source code files with proper structure\\n`;
response += `- All necessary imports and dependencies\\n`;
response += `- Production-ready error handling\\n`;
response += `- Setup and installation instructions\\n`;
response += `- Working examples and usage demonstrations\\n\\n`;
response += `## File Structure\\n`;
response += `- Organized project layout\\n`;
response += `- Configuration files\\n`;
response += `- Test files and documentation\\n\\n`;
response += `## Testing Strategy\\n`;
response += `[Comprehensive testing approach and examples]\\n\\n`;
} else if (agent.category === 'business-strategy') {
response += `## Strategic Analysis\\n`;
response += `[Business analysis and strategic recommendations]\\n\\n`;
response += `## Implementation Plan\\n`;
response += `[Step-by-step implementation roadmap]\\n\\n`;
response += `## Success Metrics\\n`;
response += `[KPIs and measurement criteria]\\n\\n`;
} else if (agent.category === 'elite-research') {
response += `## Research Methodology\\n`;
response += `[Comprehensive research approach and source strategy]\\n\\n`;
response += `## Key Findings\\n`;
response += `[Research results and insights]\\n\\n`;
response += `## Recommendations\\n`;
response += `[Actionable recommendations based on research]\\n\\n`;
}
// Note: Tool references removed for Claude Desktop compatibility
// Add next steps and coordination
if (agent.coordination?.worksWellWith?.length) {
const suggestedAgents = agent.coordination.worksWellWith.slice(0, 2);
response += `## Next Steps & Coordination\\n`;
response += `Consider consulting with the following specialists for additional expertise:\\n`;
suggestedAgents.forEach(agentName => {
response += `- **${agentName}**: For complementary capabilities\\n`;
});
response += `\\n`;
}
// Add quality assurance note for enterprise-grade agents
if (agent.quality?.enterpriseGrade) {
response += `## Quality Assurance\\n`;
response += `This solution follows enterprise standards and best practices:\\n`;
response += `- Production-ready implementation\\n`;
response += `- Comprehensive error handling\\n`;
response += `- Scalable and maintainable architecture\\n`;
response += `- Industry compliance considerations\\n`;
}
return response;
}
// Tool usage description method removed for Claude Desktop compatibility
private extractToolsUsed(response: string): string[] {
// Tool extraction disabled for Claude Desktop compatibility
return [];
}
private suggestFollowupAgents(agent: WorksonaAgent, response: string): string[] {
const suggestions: string[] = [];
// Add agents from coordination metadata
if (agent.coordination?.worksWellWith) {
suggestions.push(...agent.coordination.worksWellWith.slice(0, 2));
}
// Extract agent names mentioned in the response
const mentionedAgents = this.extractMentionedAgents(response);
suggestions.push(...mentionedAgents);
// Remove duplicates and limit to 3
return Array.from(new Set(suggestions)).slice(0, 3);
}
private extractMentionedAgents(response: string): string[] {
// Look for agent name patterns in the response
const agentPattern = /\\b([a-z-]+(?:-[a-z]+)*(?:-(?:engineer|developer|architect|specialist|manager|analyst|auditor)))\\b/g;
const matches = response.match(agentPattern) || [];
return matches.slice(0, 2);
}
private calculateConfidence(agent: WorksonaAgent, request: string, response: string): number {
let confidence = 0.7; // Base confidence
// Higher confidence for agents with matching capabilities
const requestLower = request.toLowerCase();
const matchingCapabilities = agent.capabilities.primary.filter(cap =>
requestLower.includes(cap.toLowerCase().replace(/_/g, ' '))
);
confidence += matchingCapabilities.length * 0.1;
// Higher confidence for enterprise-grade agents
if (agent.quality?.enterpriseGrade) {
confidence += 0.1;
}
// Higher confidence for longer, detailed responses
if (response.length > 1000) {
confidence += 0.1;
}
return Math.min(confidence, 1.0);
}
private estimateTokens(text: string): number {
// Rough estimation: ~4 characters per token
return Math.ceil(text.length / 4);
}
private async updateAgentMemory(
agent: WorksonaAgent,
request: string,
response: string,
context: AgentExecutionContext
): Promise<void> {
const summary = response.length > 300 ? response.substring(0, 300) + '...' : response;
context.memory.addMemory({
agentName: agent.name,
request,
response,
timestamp: new Date(),
context: context.projectContext?.name || 'general',
summary
});
}
// Utility methods for execution statistics
getExecutionStats(): {
totalExecutions: number;
averageExecutionTime: number;
successRate: number;
} {
const totalExecutions = this.executionCount;
const averageExecutionTime = this.executionTimes.length > 0
? this.executionTimes.reduce((a, b) => a + b, 0) / this.executionTimes.length
: 0;
return {
totalExecutions,
averageExecutionTime,
successRate: 1.0 // In this simulation, we assume all executions succeed
};
}
}