@oliverpople/agency-x
Version:
🚀 **Transform feature requests into production-ready code in seconds**
124 lines (107 loc) • 3.22 kB
text/typescript
export interface RetryConfig {
maxRetries: number;
baseDelay: number;
maxDelay: number;
backoffMultiplier: number;
}
export interface AgentConfig {
name: string;
dependencies: string[];
critical: boolean;
retryConfig: RetryConfig;
timeout: number;
}
export class AgentError extends Error {
constructor(
public agentName: string,
message: string,
public isRetryable: boolean = true,
public originalError?: Error
) {
super(`[${agentName}] ${message}`);
this.name = 'AgentError';
}
}
export class OrchestrationError extends Error {
constructor(
message: string,
public failedAgents: string[],
public completedAgents: string[]
) {
super(message);
this.name = 'OrchestrationError';
}
}
export const calculateDelay = (attempt: number, config: RetryConfig): number => {
const delay = config.baseDelay * Math.pow(config.backoffMultiplier, attempt - 1);
return Math.min(delay, config.maxDelay);
};
export const sleep = (ms: number): Promise<void> =>
new Promise(resolve => setTimeout(resolve, ms));
export const withRetry = async <T>(
operation: () => Promise<T>,
agentName: string,
config: RetryConfig
): Promise<T> => {
let lastError: Error | null = null;
for (let attempt = 1; attempt <= config.maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error as Error;
if (error instanceof AgentError && !error.isRetryable) {
throw error;
}
if (attempt < config.maxRetries) {
const delay = calculateDelay(attempt, config);
console.warn(`⚠️ [${agentName}] Attempt ${attempt} failed: ${error}. Retrying in ${delay}ms...`);
await sleep(delay);
}
}
}
throw new AgentError(
agentName,
`Failed after ${config.maxRetries} attempts. Last error: ${lastError?.message || 'Unknown error'}`,
false,
lastError || new Error('Unknown error')
);
};
export const withTimeout = <T>(
promise: Promise<T>,
timeoutMs: number,
agentName: string
): Promise<T> => {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject(new AgentError(agentName, `Operation timed out after ${timeoutMs}ms`, true));
}, timeoutMs);
promise
.then(resolve)
.catch(reject)
.finally(() => clearTimeout(timeoutId));
});
};
export const createAgentRunner = (config: AgentConfig) => {
return async <T>(operation: () => Promise<T>): Promise<T> => {
const timedOperation = () => withTimeout(operation(), config.timeout, config.name);
return withRetry(timedOperation, config.name, config.retryConfig);
};
};
// Default configurations
export const DEFAULT_RETRY_CONFIG: RetryConfig = {
maxRetries: 3,
baseDelay: 1000, // 1 second
maxDelay: 30000, // 30 seconds
backoffMultiplier: 2,
};
export const DEFAULT_AGENT_CONFIG: Omit<AgentConfig, 'name' | 'dependencies'> = {
critical: false,
retryConfig: DEFAULT_RETRY_CONFIG,
timeout: 90000, // 90 seconds
};
export const CRITICAL_RETRY_CONFIG: RetryConfig = {
maxRetries: 5,
baseDelay: 2000, // 2 seconds
maxDelay: 60000, // 60 seconds
backoffMultiplier: 2,
};