claude-flow-novice
Version:
Claude Flow Novice - Advanced orchestration platform for multi-agent AI workflows with CFN Loop architecture Includes Local RuVector Accelerator and all CFN skills for complete functionality.
291 lines (290 loc) • 9.97 kB
JavaScript
/**
* StandardAdapter.ts - Reference implementation of standardized integration patterns
*
* Features:
* - Standard data envelope (wrap/unwrap)
* - Standard error handling (try/catch/log/rethrow)
* - Standard logging (structured JSON)
* - Standard retry logic (exponential backoff)
* - Standard correlation tracking (task_id, agent_id)
*/ import { createHash } from 'crypto';
/**
* Default JSON logger implementation
*/ export class JSONLogger {
formatLog(level, message, context) {
return JSON.stringify({
level,
message,
timestamp: new Date().toISOString(),
...context
});
}
info(message, context) {
console.log(this.formatLog('INFO', message, context));
}
warn(message, context) {
console.warn(this.formatLog('WARN', message, context));
}
error(message, context) {
console.error(this.formatLog('ERROR', message, context));
}
debug(message, context) {
console.debug(this.formatLog('DEBUG', message, context));
}
}
/**
* StandardAdapter - Reference implementation for integration patterns
*
* @example
* ```typescript
* const adapter = new StandardAdapter({
* task_id: 'task-123',
* agent_id: 'agent-456',
* logger: new JSONLogger(),
* });
*
* // Wrap data
* const envelope = adapter.wrap({ user: 'john', action: 'login' });
*
* // Unwrap data with validation
* const data = adapter.unwrap(envelope);
*
* // Execute with retry
* const result = await adapter.withRetry(async () => {
* return await externalApiCall();
* });
* ```
*/ export class StandardAdapter {
task_id;
agent_id;
logger;
default_retry_config;
constructor(config){
this.task_id = config.task_id;
this.agent_id = config.agent_id;
this.logger = config.logger || new JSONLogger();
this.default_retry_config = {
max_attempts: 3,
initial_delay_ms: 100,
max_delay_ms: 10000,
backoff_multiplier: 2,
jitter_factor: 0.1,
...config.retry_config
};
}
/**
* Wrap payload in standard data envelope
*
* @param payload - Data to wrap
* @param metadata - Optional metadata
* @returns Standardized data envelope
*/ wrap(payload, metadata) {
const correlation_id = this.generateCorrelationId();
const timestamp = new Date().toISOString();
const content_hash = this.generateContentHash(payload);
const envelope = {
correlation_id,
task_id: this.task_id,
agent_id: this.agent_id,
timestamp,
payload,
metadata,
content_hash
};
this.logger.debug('Data wrapped in envelope', {
correlation_id,
task_id: this.task_id,
content_hash
});
return envelope;
}
/**
* Unwrap and validate data envelope
*
* @param envelope - Data envelope to unwrap
* @returns Validated payload
* @throws Error if envelope is invalid or hash mismatch
*/ unwrap(envelope) {
try {
// Validate required fields
if (!envelope.correlation_id || !envelope.task_id || !envelope.payload) {
throw new Error('Invalid envelope: missing required fields');
}
// Verify content hash
const computed_hash = this.generateContentHash(envelope.payload);
if (computed_hash !== envelope.content_hash) {
throw new Error(`Content hash mismatch: expected ${envelope.content_hash}, got ${computed_hash}`);
}
this.logger.debug('Data unwrapped from envelope', {
correlation_id: envelope.correlation_id,
task_id: envelope.task_id
});
return envelope.payload;
} catch (error) {
this.logger.error('Failed to unwrap envelope', {
error: error instanceof Error ? error.message : String(error),
correlation_id: envelope.correlation_id
});
throw error;
}
}
/**
* Execute function with retry logic and exponential backoff
*
* @param fn - Function to execute
* @param config - Optional retry configuration
* @returns Result from successful execution
* @throws Error if all retry attempts fail
*/ async withRetry(fn, config) {
const retry_config = {
...this.default_retry_config,
...config
};
let last_error = null;
let attempt = 0;
while(attempt < retry_config.max_attempts){
try {
attempt++;
this.logger.debug('Executing with retry', {
task_id: this.task_id,
attempt,
max_attempts: retry_config.max_attempts
});
const result = await fn();
if (attempt > 1) {
this.logger.info('Retry succeeded', {
task_id: this.task_id,
attempt
});
}
return result;
} catch (error) {
last_error = error instanceof Error ? error : new Error(String(error));
this.logger.warn('Execution failed, will retry', {
task_id: this.task_id,
attempt,
max_attempts: retry_config.max_attempts,
error: last_error.message
});
// Don't delay after the last attempt
if (attempt < retry_config.max_attempts) {
const delay = this.calculateBackoffDelay(attempt, retry_config);
await this.sleep(delay);
}
}
}
// All retries exhausted
this.logger.error('All retry attempts exhausted', {
task_id: this.task_id,
attempts: retry_config.max_attempts,
error: last_error?.message
});
throw last_error || new Error('All retry attempts failed');
}
/**
* Execute function with standard error handling
* Catches errors, logs them, and rethrows with additional context
*
* @param fn - Function to execute
* @param context - Error context information
* @returns Result from successful execution
* @throws Error with enriched context
*/ async withErrorHandling(fn, context = {}) {
try {
return await fn();
} catch (error) {
const error_envelope = {
correlation_id: this.generateCorrelationId(),
task_id: this.task_id,
error_code: 'EXECUTION_ERROR',
error_message: error instanceof Error ? error.message : String(error),
error_stack: error instanceof Error ? error.stack : undefined,
timestamp: new Date().toISOString(),
metadata: context
};
this.logger.error('Execution failed with error', error_envelope);
// Rethrow with enriched context
const enriched_error = new Error(error_envelope.error_message);
enriched_error.envelope = error_envelope;
throw enriched_error;
}
}
/**
* Generate unique correlation ID for request tracking
* Format: {task_id}-{timestamp}-{random}
*/ generateCorrelationId() {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 10);
return `${this.task_id}-${timestamp}-${random}`;
}
/**
* Generate SHA256 hash of payload for integrity verification
*/ generateContentHash(payload) {
const content = JSON.stringify(payload);
return createHash('sha256').update(content).digest('hex');
}
/**
* Calculate exponential backoff delay with jitter
*/ calculateBackoffDelay(attempt, config) {
// Exponential backoff: initial_delay * (multiplier ^ (attempt - 1))
const base_delay = config.initial_delay_ms * Math.pow(config.backoff_multiplier, attempt - 1);
// Cap at max delay
const capped_delay = Math.min(base_delay, config.max_delay_ms);
// Add jitter to prevent thundering herd
const jitter = capped_delay * config.jitter_factor * Math.random();
return Math.floor(capped_delay + jitter);
}
/**
* Sleep for specified milliseconds
*/ sleep(ms) {
return new Promise((resolve)=>setTimeout(resolve, ms));
}
/**
* Get current task context
*/ getContext() {
return {
task_id: this.task_id,
agent_id: this.agent_id
};
}
} /**
* USAGE EXAMPLE - Before (Ad-hoc pattern):
*
* ```typescript
* // ❌ Ad-hoc error handling, no correlation, no retry
* async function processData(data: any) {
* try {
* const result = await externalApi.send(data);
* console.log('Success:', result);
* return result;
* } catch (err) {
* console.error('Error:', err);
* throw err;
* }
* }
* ```
*
* USAGE EXAMPLE - After (Standardized pattern):
*
* ```typescript
* // ✅ Standardized with correlation, retry, structured logging
* const adapter = new StandardAdapter({
* task_id: 'data-processing-123',
* agent_id: 'processor-agent-1',
* });
*
* async function processData(data: any) {
* const envelope = adapter.wrap(data, { source: 'user_input' });
*
* const result = await adapter.withRetry(async () => {
* return await adapter.withErrorHandling(
* async () => externalApi.send(envelope),
* { operation: 'external_api_call' }
* );
* });
*
* return adapter.unwrap(result);
* }
* ```
*/
//# sourceMappingURL=StandardAdapter.js.map