shipdeck
Version:
Ship MVPs in 48 hours. Fix bugs in 30 seconds. The command deck for developers who ship.
284 lines (247 loc) • 7.67 kB
JavaScript
/**
* Base Agent Class for Shipdeck Ultimate
* Provides foundation for all specialized agents with common functionality
*/
const { AnthropicClient } = require('../anthropic/client');
class BaseAgent {
constructor(options = {}) {
this.name = options.name || this.constructor.name;
this.description = options.description || 'Base agent for common functionality';
this.version = options.version || '1.0.0';
// Initialize Anthropic client
this.client = options.anthropicClient || new AnthropicClient(options.anthropicConfig);
// Agent configuration
this.config = {
maxRetries: 3,
temperature: 0.7,
maxTokens: 4096,
...options.config
};
// Validation
this.validateConfig();
}
/**
* Validate agent configuration
*/
validateConfig() {
if (!this.client) {
throw new Error('Anthropic client is required');
}
if (typeof this.config.maxRetries !== 'number' || this.config.maxRetries < 0) {
throw new Error('maxRetries must be a non-negative number');
}
}
/**
* Get agent capabilities - to be overridden by specialized agents
* @returns {Array<string>} Array of capability strings
*/
getCapabilities() {
return ['base'];
}
/**
* Get agent metadata
* @returns {Object} Agent metadata
*/
getMetadata() {
return {
name: this.name,
description: this.description,
version: this.version,
capabilities: this.getCapabilities(),
config: {
maxRetries: this.config.maxRetries,
temperature: this.config.temperature,
maxTokens: this.config.maxTokens
}
};
}
/**
* Execute agent task - must be implemented by specialized agents
* @param {Object} task - Task configuration
* @param {Object} context - Execution context
* @returns {Promise<Object>} Execution result
*/
async execute(task, context = {}) {
throw new Error('execute() method must be implemented by specialized agents');
}
/**
* Validate task input
* @param {Object} task - Task to validate
* @returns {boolean} True if valid
*/
validateTask(task) {
if (!task || typeof task !== 'object') {
throw new Error('Task must be an object');
}
if (!task.type) {
throw new Error('Task must have a type property');
}
return true;
}
/**
* Create standardized prompt for the agent
* @param {Object} task - Task configuration
* @param {Object} context - Additional context
* @returns {string} Formatted prompt
*/
createPrompt(task, context = {}) {
const systemPrompt = this.getSystemPrompt();
const taskPrompt = this.formatTaskPrompt(task);
const contextPrompt = context ? this.formatContextPrompt(context) : '';
return [systemPrompt, taskPrompt, contextPrompt].filter(Boolean).join('\n\n');
}
/**
* Get system prompt - to be overridden by specialized agents
* @returns {string} System prompt
*/
getSystemPrompt() {
return `You are ${this.name}, a specialized AI agent.
Description: ${this.description}
Capabilities: ${this.getCapabilities().join(', ')}
Please provide helpful, accurate, and well-structured responses.`;
}
/**
* Format task into prompt
* @param {Object} task - Task configuration
* @returns {string} Formatted task prompt
*/
formatTaskPrompt(task) {
return `Task Type: ${task.type}
${task.description ? `Description: ${task.description}` : ''}
${task.requirements ? `Requirements:\n${Array.isArray(task.requirements) ? task.requirements.map(r => `- ${r}`).join('\n') : task.requirements}` : ''}
${task.input ? `Input:\n${typeof task.input === 'object' ? JSON.stringify(task.input, null, 2) : task.input}` : ''}`;
}
/**
* Format context into prompt
* @param {Object} context - Context information
* @returns {string} Formatted context prompt
*/
formatContextPrompt(context) {
if (!context || Object.keys(context).length === 0) {
return '';
}
const contextEntries = Object.entries(context)
.map(([key, value]) => `${key}: ${typeof value === 'object' ? JSON.stringify(value, null, 2) : value}`)
.join('\n');
return `Context:\n${contextEntries}`;
}
/**
* Send message to Anthropic API with error handling
* @param {string|Array} messages - Message(s) to send
* @param {Object} options - Request options
* @returns {Promise<Object>} API response
*/
async sendMessage(messages, options = {}) {
try {
const messageArray = Array.isArray(messages) ? messages : [{ role: 'user', content: messages }];
const response = await this.client.createMessage(messageArray, {
maxTokens: options.maxTokens || this.config.maxTokens,
temperature: options.temperature ?? this.config.temperature,
...options
});
return {
success: true,
content: response.content[0]?.text || '',
usage: response.usage,
metadata: response._metadata
};
} catch (error) {
return {
success: false,
error: error.message,
errorType: error.constructor.name,
retryable: error.isRetryable ? error.isRetryable() : false
};
}
}
/**
* Execute task with retry logic
* @param {Object} task - Task to execute
* @param {Object} context - Execution context
* @returns {Promise<Object>} Execution result
*/
async executeWithRetry(task, context = {}) {
this.validateTask(task);
let lastError;
for (let attempt = 1; attempt <= this.config.maxRetries; attempt++) {
try {
const result = await this.execute(task, { ...context, attempt });
return {
success: true,
result,
metadata: {
agent: this.name,
attempt,
timestamp: new Date().toISOString()
}
};
} catch (error) {
lastError = error;
if (attempt < this.config.maxRetries) {
const delay = Math.pow(2, attempt - 1) * 1000; // Exponential backoff
await this.sleep(delay);
}
}
}
return {
success: false,
error: lastError.message,
metadata: {
agent: this.name,
maxAttempts: this.config.maxRetries,
timestamp: new Date().toISOString()
}
};
}
/**
* Sleep utility for retry delays
* @param {number} ms - Milliseconds to sleep
* @returns {Promise}
*/
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Log message with agent context
* @param {string} level - Log level (info, warn, error)
* @param {string} message - Message to log
* @param {Object} data - Additional data
*/
log(level, message, data = {}) {
const timestamp = new Date().toISOString();
const logEntry = {
timestamp,
agent: this.name,
level,
message,
...data
};
console[level] || console.log(`[${timestamp}] [${this.name}] [${level.toUpperCase()}] ${message}`, data);
}
/**
* Parse and structure response from AI
* @param {string} response - Raw AI response
* @returns {Object} Structured response
*/
parseResponse(response) {
try {
// Try to parse as JSON first
return JSON.parse(response);
} catch {
// Return as structured text
return {
type: 'text',
content: response,
parsed: false
};
}
}
/**
* Cleanup resources
*/
cleanup() {
// Override in specialized agents if needed
this.log('info', 'Agent cleanup completed');
}
}
module.exports = { BaseAgent };