visaire
Version:
A fully autonomous agentic CLI tool for interacting with large language models (Claude, Gemini, GPT) that can execute multi-step tasks automatically
599 lines (519 loc) • 19.5 kB
JavaScript
const Agent = require('./core/Agent');
const Providers = require('./providers');
/**
* Visaire Agent implementation with Forge-style sophisticated reasoning
* This replaces the old agent.js with advanced capabilities
*/
class EnhancedAgent extends Agent {
constructor(options = {}) {
// Initialize with enhanced defaults
const enhancedOptions = {
name: 'visaire-agent',
model: options.model || 'claude-3-sonnet-20240229',
provider: options.provider || 'claude',
temperature: options.temperature || 0.7,
maxTokens: options.maxTokens || 4000,
reasoning: {
effort: options.effort || 'medium',
maxIterations: options.maxIterations || 10,
enableReflection: options.enableReflection !== false,
enablePlanning: options.enablePlanning !== false
},
tools: {
enabled: options.toolsEnabled !== false,
autoApprove: options.autoApprove !== false, // Default to true for immediate execution
confirmationRequired: false, // No confirmation required for tool execution
maxConcurrent: options.maxConcurrent || 3
},
context: {
maxSize: options.maxContextSize || 100000,
compressionThreshold: options.compressionThreshold || 0.8,
retentionStrategy: options.retentionStrategy || 'importance'
},
security: options.security || {
allowedPaths: ['./'],
blockedCommands: ['rm -rf', 'sudo', 'format'],
maxFileSize: 10485760, // 10MB
sandboxMode: false
},
logging: {
level: options.logLevel || 'info',
enableMetrics: options.enableMetrics !== false,
enableTracing: options.enableTracing || false
},
...options
};
super(enhancedOptions);
// Store provider configuration
this.providerConfig = {
provider: enhancedOptions.provider,
apiKey: options.apiKey,
model: enhancedOptions.model,
temperature: enhancedOptions.temperature,
maxTokens: enhancedOptions.maxTokens
};
// Initialize providers
this.providers = new Providers({
timeout: options.timeout || 30000,
maxRetries: options.maxRetries || 3
});
}
/**
* Process user prompt with enhanced reasoning
*/
async processPrompt(prompt, options = {}) {
try {
// Validate inputs
if (!prompt || typeof prompt !== 'string' || prompt.trim().length === 0) {
throw new Error('Prompt is required and must be a non-empty string');
}
// Merge options with defaults
const processOptions = {
provider: options.provider || this.providerConfig.provider,
apiKey: options.apiKey || this.providerConfig.apiKey,
model: options.model || this.providerConfig.model,
temperature: options.temperature || this.providerConfig.temperature,
maxTokens: options.maxTokens || this.providerConfig.maxTokens,
autoApprove: options.autoApprove !== false, // Default to true for immediate execution
effort: options.effort || this.config.reasoning.effort,
...options
};
this.logger.info('Processing prompt with visaire agent', {
promptLength: prompt.length,
provider: processOptions.provider,
model: processOptions.model,
effort: processOptions.effort
});
// Step 1: Get LLM response with context
const llmResponse = await this.getLLMResponse(prompt, processOptions);
// Step 2: Parse actions from the response
const parsedActions = this.parseActionsFromResponse(llmResponse);
// Step 3: Process with enhanced reasoning (but override actions)
const result = await this.processInput(prompt, {
llmResponse,
overrideActions: parsedActions,
...processOptions
});
// Step 3: Format final response
const formattedResult = this.formatResult(result, llmResponse);
this.logger.info('Prompt processing completed', {
success: formattedResult.success,
actionsExecuted: formattedResult.executionResults?.summary?.completed || 0,
processingTime: formattedResult.metrics?.processingTime
});
return formattedResult;
} catch (error) {
this.logger.error('Prompt processing failed', {
error: error.message,
stack: error.stack
});
return {
success: false,
error: error.message,
prompt,
response: null,
executionResults: null,
metrics: {
processingTime: 0,
toolCalls: 0,
tokensUsed: 0
}
};
}
}
/**
* Get LLM response with enhanced prompting
*/
async getLLMResponse(prompt, options) {
try {
// Build context-aware prompt
const enhancedPrompt = await this.buildEnhancedPrompt(prompt, options);
// Make LLM call
const response = await this.providers.call(
options.provider,
options.apiKey,
enhancedPrompt,
{
model: options.model,
temperature: options.temperature,
maxTokens: options.maxTokens
}
);
this.logger.debug('LLM response received', {
provider: options.provider,
model: options.model,
responseLength: response.length
});
return response;
} catch (error) {
this.logger.error('LLM response failed', {
provider: options.provider,
error: error.message
});
throw error;
}
}
/**
* Parse actions from LLM response
*/
parseActionsFromResponse(response) {
const actions = [];
// Look for ACTIONS section
const actionsMatch = response.match(/ACTIONS:\s*([\s\S]*?)(?=\n\n|\n[A-Z]|$)/i);
if (!actionsMatch) {
return actions;
}
const actionsSection = actionsMatch[1];
const actionLines = actionsSection.split('\n')
.map(line => line.trim())
.filter(line => line.startsWith('-'));
for (const line of actionLines) {
const actionText = line.substring(1).trim(); // Remove the '-' prefix
if (actionText.startsWith('CREATE_FILE:') || actionText.startsWith('WRITE_FILE:')) {
const match = actionText.match(/(CREATE_FILE|WRITE_FILE):\s*([^|]+)\s*\|\s*(.+)/);
if (match) {
const [, actionType, filePath, content] = match;
actions.push({
id: `action_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
tool: 'filesystem',
type: actionType.toLowerCase(),
parameters: [filePath.trim(), content.trim()],
description: `${actionType.toLowerCase().replace('_', ' ')} ${filePath.trim()}`
});
}
} else if (actionText.startsWith('READ_FILE:')) {
const match = actionText.match(/READ_FILE:\s*(.+)/);
if (match) {
const filePath = match[1].trim();
actions.push({
id: `action_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
tool: 'filesystem',
type: 'read_file',
parameters: [filePath],
description: `read file ${filePath}`
});
}
} else if (actionText.startsWith('CREATE_FOLDER:')) {
const match = actionText.match(/CREATE_FOLDER:\s*(.+)/);
if (match) {
const folderPath = match[1].trim();
actions.push({
id: `action_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
tool: 'filesystem',
type: 'create_directory',
parameters: [folderPath],
description: `create folder ${folderPath}`
});
}
} else if (actionText.startsWith('LIST_DIRECTORY:')) {
const match = actionText.match(/LIST_DIRECTORY:\s*(.+)/);
if (match) {
const dirPath = match[1].trim();
actions.push({
id: `action_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
tool: 'filesystem',
type: 'list_directory',
parameters: [dirPath],
description: `list directory ${dirPath}`
});
}
} else if (actionText.startsWith('RUN_COMMAND:')) {
const match = actionText.match(/RUN_COMMAND:\s*(.+)/);
if (match) {
const command = match[1].trim();
actions.push({
id: `action_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
tool: 'exec',
type: 'run_command',
parameters: [command],
description: `run command: ${command}`
});
}
} else if (actionText.startsWith('INSTALL_PACKAGE:')) {
const match = actionText.match(/INSTALL_PACKAGE:\s*(.+)/);
if (match) {
const packageName = match[1].trim();
actions.push({
id: `action_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
tool: 'exec',
type: 'install_package',
parameters: [`npm install ${packageName}`],
description: `install package: ${packageName}`
});
}
}
}
return actions;
}
/**
* Build enhanced prompt with context and reasoning instructions
*/
async buildEnhancedPrompt(userPrompt, options) {
// Get current context
const context = await this.contextManager.buildContext({
input: userPrompt,
options
});
// Build system prompt based on effort level
const systemPrompt = this.buildSystemPrompt(options.effort, context);
// Combine system prompt with user prompt
const enhancedPrompt = `${systemPrompt}
User Request: ${userPrompt}
Please analyze this request carefully and provide a comprehensive response. If the request requires actions to be taken (like creating files, running commands, or making changes), format your response as follows:
1. First, provide your analysis and reasoning
2. Then, if actions are needed, include an ACTIONS section with specific commands:
ACTIONS:
[List specific actions using these formats:]
- CREATE_FILE: path/to/file.txt | content here
- WRITE_FILE: path/to/file.txt | content here
- READ_FILE: path/to/file.txt
- CREATE_FOLDER: path/to/folder
- LIST_DIRECTORY: path/to/directory
- RUN_COMMAND: command to execute
- INSTALL_PACKAGE: package-name
Example:
ACTIONS:
- CREATE_FOLDER: test
- CREATE_FILE: test/index.html | <html><body>Hello World</body></html>
- RUN_COMMAND: ls -la
If no actions are needed, just provide your response without an ACTIONS section.`;
this.logger.debug('Enhanced prompt built', {
systemPromptLength: systemPrompt.length,
totalPromptLength: enhancedPrompt.length,
contextSections: Object.keys(context.sections).length
});
return enhancedPrompt;
}
/**
* Build system prompt based on effort level and context
*/
buildSystemPrompt(effort, context) {
const basePrompt = `You are Visaire, an advanced AI agent with sophisticated reasoning capabilities. You can analyze requests, plan actions, and execute tasks autonomously.
Current Context:
- Working Directory: ${context.workingDirectory}
- Available Files: ${context.sections.fileSystem?.files?.length || 0} files
- Project Type: ${context.sections.fileSystem?.projectStructure?.patterns?.join(', ') || 'Unknown'}
- Dependencies: ${context.sections.dependencies?.installedPackages?.length || 0} packages installed
Capabilities:
- File Operations: Create, read, modify, delete files and directories
- Command Execution: Run shell commands and install packages
- Code Analysis: Analyze code structure and dependencies
- Network Operations: Make HTTP requests and download files
`;
// Add effort-specific instructions
switch (effort) {
case 'low':
return basePrompt + `Instructions:
- Provide direct, simple responses
- Execute only the most essential actions
- Minimize complexity and processing time`;
case 'medium':
return basePrompt + `Instructions:
- Analyze the request thoroughly
- Plan actions before executing
- Provide detailed explanations
- Consider potential issues and alternatives`;
case 'high':
return basePrompt + `Instructions:
- Perform deep analysis of the request
- Create comprehensive execution plans
- Consider multiple approaches and trade-offs
- Reflect on results and suggest improvements
- Anticipate edge cases and error scenarios`;
case 'maximum':
return basePrompt + `Instructions:
- Conduct exhaustive analysis of all aspects
- Generate multiple alternative approaches
- Perform detailed risk assessment
- Create step-by-step execution plans with contingencies
- Continuously reflect and optimize throughout the process
- Provide comprehensive documentation and explanations`;
default:
return basePrompt + `Instructions:
- Analyze the request and plan appropriate actions
- Execute tasks efficiently and safely
- Provide clear explanations of what was done`;
}
}
/**
* Format final result for user
*/
formatResult(processingResult, llmResponse) {
const result = {
success: processingResult.success,
response: llmResponse,
reasoning: processingResult.reasoning,
executionResults: processingResult.executionResults,
metrics: processingResult.metrics,
conversationId: processingResult.conversationId
};
// Add summary information
if (processingResult.executionResults) {
result.summary = {
actionsPlanned: processingResult.reasoning?.actions?.length || 0,
actionsExecuted: processingResult.executionResults.summary?.completed || 0,
filesCreated: this.countFileActions(processingResult.executionResults, 'create'),
filesModified: this.countFileActions(processingResult.executionResults, 'modify'),
commandsRun: this.countToolActions(processingResult.executionResults, 'exec'),
errors: processingResult.executionResults.summary?.failed || 0
};
}
return result;
}
/**
* Count file actions in execution results
*/
countFileActions(executionResults, actionType) {
if (!executionResults.results) return 0;
return executionResults.results.filter(result =>
result.action?.tool === 'filesystem' &&
result.action?.type?.includes(actionType)
).length;
}
/**
* Count tool actions in execution results
*/
countToolActions(executionResults, toolName) {
if (!executionResults.results) return 0;
return executionResults.results.filter(result =>
result.action?.tool === toolName
).length;
}
/**
* Start autonomous session (enhanced version)
*/
async startAutonomousSession(prompt, options = {}) {
let iteration = 0; // Declare iteration at function scope
try {
this.logger.info('Starting autonomous session', {
promptLength: prompt.length,
maxIterations: options.maxIterations || 10
});
const sessionOptions = {
maxIterations: options.maxIterations || 10,
autoApprove: true, // Autonomous mode always auto-approves tool and command actions
effort: options.effort || 'high', // Use high effort for autonomous mode
...options
};
let currentPrompt = prompt;
const maxIterations = sessionOptions.maxIterations;
const results = [];
while (iteration < maxIterations) {
iteration++;
this.logger.info(`Autonomous iteration ${iteration}/${maxIterations}`);
// Process current prompt
const result = await this.processPrompt(currentPrompt, sessionOptions);
results.push(result);
// Check if task is complete
if (this.isTaskComplete(result)) {
this.logger.info('Autonomous session completed successfully', {
iterations: iteration,
totalActions: results.reduce((sum, r) => sum + (r.summary?.actionsExecuted || 0), 0)
});
break;
}
// Generate continuation prompt
currentPrompt = this.generateContinuationPrompt(prompt, result, iteration);
}
return {
success: true,
iterations: iteration,
results,
summary: this.summarizeAutonomousSession(results)
};
} catch (error) {
this.logger.error('Autonomous session failed', {
error: error.message,
iteration: iteration
});
throw error;
}
}
/**
* Check if autonomous task is complete
*/
isTaskComplete(result) {
if (!result.success) return false;
// Check if no more actions are planned
if (!result.reasoning?.actions || result.reasoning.actions.length === 0) {
return true;
}
// Check for completion indicators in response
const response = result.response?.toLowerCase() || '';
const completionIndicators = [
'task completed', 'task is complete', 'finished', 'done',
'successfully completed', 'all steps completed'
];
return completionIndicators.some(indicator => response.includes(indicator));
}
/**
* Generate continuation prompt for autonomous session
*/
generateContinuationPrompt(originalPrompt, lastResult, iteration) {
const executedActions = lastResult.summary?.actionsExecuted || 0;
const errors = lastResult.summary?.errors || 0;
let continuationPrompt = `Continue with the original task: "${originalPrompt}"
Progress Update (Iteration ${iteration}):
- Actions executed: ${executedActions}
- Errors encountered: ${errors}`;
if (lastResult.summary?.filesCreated > 0) {
continuationPrompt += `\n- Files created: ${lastResult.summary.filesCreated}`;
}
if (lastResult.summary?.commandsRun > 0) {
continuationPrompt += `\n- Commands executed: ${lastResult.summary.commandsRun}`;
}
continuationPrompt += `\n\nWhat is the next step to complete this task? If the task is already complete, please confirm completion.`;
return continuationPrompt;
}
/**
* Summarize autonomous session results
*/
summarizeAutonomousSession(results) {
const summary = {
totalIterations: results.length,
totalActions: 0,
totalFiles: 0,
totalCommands: 0,
totalErrors: 0,
processingTime: 0,
success: results.every(r => r.success)
};
for (const result of results) {
if (result.summary) {
summary.totalActions += result.summary.actionsExecuted || 0;
summary.totalFiles += (result.summary.filesCreated || 0) + (result.summary.filesModified || 0);
summary.totalCommands += result.summary.commandsRun || 0;
summary.totalErrors += result.summary.errors || 0;
}
if (result.metrics) {
summary.processingTime += result.metrics.processingTime || 0;
}
}
return summary;
}
/**
* Get visaire agent status
*/
getEnhancedStatus() {
const baseStatus = this.getStatus();
return {
...baseStatus,
version: '2.0.0',
type: 'enhanced',
capabilities: [
'sophisticated_reasoning',
'context_awareness',
'autonomous_execution',
'code_intelligence',
'multi_step_planning',
'reflection_and_adjustment'
],
providerConfig: {
provider: this.providerConfig.provider,
model: this.providerConfig.model,
hasApiKey: !!this.providerConfig.apiKey
}
};
}
}
module.exports = EnhancedAgent;