UNPKG

jay-code

Version:

Streamlined AI CLI orchestration engine with mathematical rigor and enterprise-grade reliability

1,026 lines (885 loc) 25.4 kB
/** * Self-improving workflow hooks for agentic-flow * * Enables adaptive workflows with provider selection * and continuous improvement based on outcomes. */ import { agenticHookManager } from './hook-manager.js'; import type { AgenticHookContext, HookHandlerResult, WorkflowHookPayload, WorkflowDecision, Learning, SideEffect, Pattern, } from './types.js'; // ===== Workflow Start Hook ===== export const workflowStartHook = { id: 'agentic-workflow-start', type: 'workflow-start' as const, priority: 100, handler: async ( payload: WorkflowHookPayload, context: AgenticHookContext ): Promise<HookHandlerResult> => { const { workflowId, state } = payload; const sideEffects: SideEffect[] = []; // Load workflow history and learnings const history = await loadWorkflowHistory(workflowId, context); const learnings = await loadWorkflowLearnings(workflowId, context); // Select optimal provider based on history const provider = await selectOptimalProvider( workflowId, state, history, context ); // Initialize workflow state const enhancedState = { ...state, startTime: Date.now(), provider, learnings: learnings.slice(-10), // Last 10 learnings predictions: await generateWorkflowPredictions(workflowId, state, context), }; // Store workflow session sideEffects.push({ type: 'memory', action: 'store', data: { key: `workflow:session:${workflowId}:${context.sessionId}`, value: enhancedState, ttl: 86400, // 24 hours }, }); // Track workflow start sideEffects.push({ type: 'metric', action: 'increment', data: { name: `workflow.starts.${workflowId}` }, }); return { continue: true, modified: true, payload: { ...payload, state: enhancedState, }, sideEffects, }; }, }; // ===== Workflow Step Hook ===== export const workflowStepHook = { id: 'agentic-workflow-step', type: 'workflow-step' as const, priority: 100, handler: async ( payload: WorkflowHookPayload, context: AgenticHookContext ): Promise<HookHandlerResult> => { const { workflowId, step, state } = payload; if (!step) { return { continue: true }; } const sideEffects: SideEffect[] = []; // Measure step performance const stepStart = Date.now(); // Check for step optimizations const optimizations = await getStepOptimizations( workflowId, step, context ); if (optimizations.length > 0) { // Apply step optimizations const optimizedState = applyStepOptimizations( state, optimizations ); sideEffects.push({ type: 'log', action: 'write', data: { level: 'info', message: `Applied ${optimizations.length} optimizations to step ${step}`, data: { optimizations }, }, }); return { continue: true, modified: true, payload: { ...payload, state: optimizedState, }, sideEffects, }; } // Track step execution sideEffects.push({ type: 'memory', action: 'store', data: { key: `workflow:step:${workflowId}:${step}:${Date.now()}`, value: { step, state: summarizeState(state), timestamp: Date.now(), }, ttl: 86400, }, }); return { continue: true, sideEffects, }; }, }; // ===== Workflow Decision Hook ===== export const workflowDecisionHook = { id: 'agentic-workflow-decision', type: 'workflow-decision' as const, priority: 90, handler: async ( payload: WorkflowHookPayload, context: AgenticHookContext ): Promise<HookHandlerResult> => { const { workflowId, decision, state } = payload; if (!decision) { return { continue: true }; } const sideEffects: SideEffect[] = []; // Enhance decision with historical data const historicalOutcomes = await getDecisionOutcomes( workflowId, decision.point, context ); // Calculate confidence adjustments const adjustedDecision = adjustDecisionConfidence( decision, historicalOutcomes ); // Generate alternative paths const alternatives = await generateAlternativeDecisions( workflowId, decision, state, context ); if (alternatives.length > 0) { // Check if better alternative exists const bestAlternative = alternatives.find(alt => alt.confidence > adjustedDecision.confidence * 1.2 ); if (bestAlternative) { sideEffects.push({ type: 'notification', action: 'emit', data: { event: 'workflow:decision:alternative', data: { original: adjustedDecision, suggested: bestAlternative, }, }, }); // Override with better decision adjustedDecision.selected = bestAlternative.selected; adjustedDecision.confidence = bestAlternative.confidence; adjustedDecision.reasoning = `${adjustedDecision.reasoning} (AI-optimized)`; } } // Store decision for learning sideEffects.push({ type: 'memory', action: 'store', data: { key: `decision:${workflowId}:${decision.point}:${Date.now()}`, value: { ...adjustedDecision, alternatives, state: summarizeState(state), }, ttl: 604800, // 7 days }, }); // Track decision metrics sideEffects.push({ type: 'metric', action: 'update', data: { name: `workflow.decisions.confidence.${workflowId}`, value: adjustedDecision.confidence, }, }); return { continue: true, modified: true, payload: { ...payload, decision: adjustedDecision, }, sideEffects, }; }, }; // ===== Workflow Complete Hook ===== export const workflowCompleteHook = { id: 'agentic-workflow-complete', type: 'workflow-complete' as const, priority: 100, handler: async ( payload: WorkflowHookPayload, context: AgenticHookContext ): Promise<HookHandlerResult> => { const { workflowId, state, metrics } = payload; const sideEffects: SideEffect[] = []; // Calculate workflow performance const performance = calculateWorkflowPerformance(state, metrics); // Extract learnings from this execution const learnings = await extractWorkflowLearnings( workflowId, state, performance, context ); // Store learnings for (const learning of learnings) { sideEffects.push({ type: 'memory', action: 'store', data: { key: `learning:${workflowId}:${learning.type}:${Date.now()}`, value: learning, ttl: 0, // Permanent }, }); } // Update workflow success patterns if (performance.success) { const pattern: Pattern = { id: `workflow_success_${Date.now()}`, type: 'success', confidence: performance.score, occurrences: 1, context: { workflowId, provider: state.provider, duration: metrics?.duration || 0, decisions: countDecisions(state), }, }; context.neural.patterns.add(pattern); sideEffects.push({ type: 'neural', action: 'train', data: { patterns: [pattern], modelId: `workflow-optimizer-${workflowId}`, }, }); } // Generate improvement suggestions const improvements = await generateImprovementSuggestions( workflowId, state, performance, learnings, context ); if (improvements.length > 0) { sideEffects.push({ type: 'notification', action: 'emit', data: { event: 'workflow:improvements:suggested', data: { workflowId, improvements, performance, }, }, }); } // Update workflow metrics sideEffects.push( { type: 'metric', action: 'update', data: { name: `workflow.completion.rate.${workflowId}`, value: performance.success ? 1 : 0, }, }, { type: 'metric', action: 'update', data: { name: `workflow.performance.score.${workflowId}`, value: performance.score, }, } ); return { continue: true, sideEffects, }; }, }; // ===== Workflow Error Hook ===== export const workflowErrorHook = { id: 'agentic-workflow-error', type: 'workflow-error' as const, priority: 95, handler: async ( payload: WorkflowHookPayload, context: AgenticHookContext ): Promise<HookHandlerResult> => { const { workflowId, error, state } = payload; if (!error) { return { continue: true }; } const sideEffects: SideEffect[] = []; // Analyze error pattern const errorPattern = await analyzeErrorPattern( workflowId, error, state, context ); // Store error for learning sideEffects.push({ type: 'memory', action: 'store', data: { key: `error:${workflowId}:${Date.now()}`, value: { error: { message: error.message, stack: error.stack, type: error.name, }, pattern: errorPattern, state: summarizeState(state), timestamp: Date.now(), }, ttl: 604800, // 7 days }, }); // Check for recovery strategies const recovery = await findRecoveryStrategy( workflowId, error, errorPattern, context ); if (recovery) { sideEffects.push({ type: 'log', action: 'write', data: { level: 'info', message: 'Recovery strategy found', data: recovery, }, }); // Apply recovery const recoveredState = applyRecoveryStrategy(state, recovery); return { continue: true, modified: true, payload: { ...payload, state: recoveredState, error: undefined, // Clear error after recovery }, sideEffects, }; } // Learn from failure const failureLearning: Learning = { type: 'failure', context: `Error in workflow ${workflowId}: ${error.message}`, value: { errorType: error.name, state: summarizeState(state), pattern: errorPattern, }, applicability: errorPattern.confidence, }; sideEffects.push({ type: 'memory', action: 'store', data: { key: `learning:failure:${workflowId}:${Date.now()}`, value: failureLearning, ttl: 0, // Permanent }, }); return { continue: true, sideEffects, }; }, }; // ===== Helper Functions ===== async function loadWorkflowHistory( workflowId: string, context: AgenticHookContext ): Promise<any[]> { const historyKey = `workflow:history:${workflowId}`; return await context.memory.cache.get(historyKey) || []; } async function loadWorkflowLearnings( workflowId: string, context: AgenticHookContext ): Promise<Learning[]> { const learningsKey = `workflow:learnings:${workflowId}`; return await context.memory.cache.get(learningsKey) || []; } async function selectOptimalProvider( workflowId: string, state: any, history: any[], context: AgenticHookContext ): Promise<string> { // Analyze historical performance by provider const providerStats = new Map<string, { success: number; total: number }>(); for (const execution of history) { const provider = execution.provider; if (!provider) continue; const stats = providerStats.get(provider) || { success: 0, total: 0 }; stats.total++; if (execution.success) stats.success++; providerStats.set(provider, stats); } // Calculate success rates let bestProvider = 'openai'; // Default let bestRate = 0; for (const [provider, stats] of providerStats) { const rate = stats.success / stats.total; if (rate > bestRate && stats.total >= 5) { bestRate = rate; bestProvider = provider; } } // Check current provider health const healthKey = `provider:health:${bestProvider}`; const health = await context.memory.cache.get(healthKey); if (health && health.score < 0.5) { // Provider unhealthy, select alternative return selectAlternativeProvider(bestProvider, providerStats); } return bestProvider; } async function generateWorkflowPredictions( workflowId: string, state: any, context: AgenticHookContext ): Promise<any> { // Generate predictions for workflow execution const predictions = { estimatedDuration: 0, successProbability: 0.7, likelyBottlenecks: [], recommendedOptimizations: [], }; // Load historical durations const history = await loadWorkflowHistory(workflowId, context); if (history.length > 0) { const durations = history .filter(h => h.duration) .map(h => h.duration); if (durations.length > 0) { predictions.estimatedDuration = durations.reduce((a, b) => a + b, 0) / durations.length; } const successes = history.filter(h => h.success).length; predictions.successProbability = successes / history.length; } return predictions; } async function getStepOptimizations( workflowId: string, step: string, context: AgenticHookContext ): Promise<any[]> { // Get optimizations for specific step const optKey = `optimizations:${workflowId}:${step}`; return await context.memory.cache.get(optKey) || []; } function applyStepOptimizations( state: any, optimizations: any[] ): any { let optimizedState = { ...state }; for (const opt of optimizations) { switch (opt.type) { case 'skip': if (opt.condition && opt.condition(state)) { optimizedState.skipSteps = [ ...(optimizedState.skipSteps || []), opt.target, ]; } break; case 'parallel': optimizedState.parallelSteps = [ ...(optimizedState.parallelSteps || []), ...opt.steps, ]; break; case 'cache': optimizedState.useCache = true; optimizedState.cacheKeys = [ ...(optimizedState.cacheKeys || []), opt.key, ]; break; } } return optimizedState; } function summarizeState(state: any): any { // Create summary of state for storage return { keys: Object.keys(state), size: JSON.stringify(state).length, hasError: !!state.error, provider: state.provider, timestamp: Date.now(), }; } async function getDecisionOutcomes( workflowId: string, decisionPoint: string, context: AgenticHookContext ): Promise<any[]> { // Get historical outcomes for decision point const outcomeKey = `outcomes:${workflowId}:${decisionPoint}`; return await context.memory.cache.get(outcomeKey) || []; } function adjustDecisionConfidence( decision: WorkflowDecision, historicalOutcomes: any[] ): WorkflowDecision { if (historicalOutcomes.length === 0) { return decision; } // Calculate success rate for selected option const relevantOutcomes = historicalOutcomes.filter(o => o.selected === decision.selected ); if (relevantOutcomes.length === 0) { return decision; } const successRate = relevantOutcomes.filter(o => o.success).length / relevantOutcomes.length; // Adjust confidence based on historical success const adjustedConfidence = decision.confidence * 0.7 + successRate * 0.3; return { ...decision, confidence: adjustedConfidence, learnings: [ ...decision.learnings, { type: 'success', context: `Historical success rate: ${(successRate * 100).toFixed(1)}%`, value: successRate, applicability: Math.min(relevantOutcomes.length / 10, 1), }, ], }; } async function generateAlternativeDecisions( workflowId: string, decision: WorkflowDecision, state: any, context: AgenticHookContext ): Promise<WorkflowDecision[]> { // Generate alternative decision paths const alternatives: WorkflowDecision[] = []; // Check each option not selected for (const option of decision.options) { if (option === decision.selected) continue; // Calculate alternative confidence const altConfidence = await calculateAlternativeConfidence( workflowId, decision.point, option, state, context ); if (altConfidence > 0.5) { alternatives.push({ ...decision, selected: option, confidence: altConfidence, reasoning: `Alternative path based on historical analysis`, }); } } return alternatives; } function calculateWorkflowPerformance( state: any, metrics: any ): any { const performance = { success: !state.error, score: 0, duration: metrics?.duration || 0, efficiency: 0, reliability: 0, }; // Calculate performance score if (performance.success) { performance.score = 0.7; // Base success score // Adjust for duration if (metrics?.duration && state.predictions?.estimatedDuration) { const durationRatio = state.predictions.estimatedDuration / metrics.duration; performance.efficiency = Math.min(durationRatio, 1); performance.score += performance.efficiency * 0.2; } // Adjust for error rate if (metrics?.errorRate !== undefined) { performance.reliability = 1 - metrics.errorRate; performance.score += performance.reliability * 0.1; } } return performance; } async function extractWorkflowLearnings( workflowId: string, state: any, performance: any, context: AgenticHookContext ): Promise<Learning[]> { const learnings: Learning[] = []; // Learn from successful execution if (performance.success) { learnings.push({ type: 'success', context: `Successful workflow execution with score ${performance.score}`, value: { provider: state.provider, duration: performance.duration, decisions: extractDecisions(state), }, applicability: performance.score, }); } // Learn from optimizations if (state.appliedOptimizations) { for (const opt of state.appliedOptimizations) { learnings.push({ type: 'optimization', context: `Applied ${opt.type} optimization at ${opt.step}`, value: opt, applicability: 0.8, }); } } return learnings; } function countDecisions(state: any): number { // Count decisions made during workflow return state.decisions?.length || 0; } async function generateImprovementSuggestions( workflowId: string, state: any, performance: any, learnings: Learning[], context: AgenticHookContext ): Promise<any[]> { const suggestions: any[] = []; // Suggest caching if repeated operations if (performance.duration > 5000) { suggestions.push({ type: 'cache', target: 'frequent_operations', reason: 'Long execution time detected', expectedImprovement: '30-50% reduction in duration', }); } // Suggest parallelization if (state.sequentialSteps?.length > 3) { suggestions.push({ type: 'parallel', target: 'independent_steps', reason: 'Multiple sequential steps detected', expectedImprovement: '40-60% reduction in duration', }); } // Suggest provider switch based on learnings const providerLearnings = learnings.filter(l => l.type === 'success' && l.value.provider ); if (providerLearnings.length > 0) { const providerScores = new Map<string, number>(); for (const learning of providerLearnings) { const provider = learning.value.provider; const score = providerScores.get(provider) || 0; providerScores.set(provider, score + learning.applicability); } const currentScore = providerScores.get(state.provider) || 0; for (const [provider, score] of providerScores) { if (score > currentScore * 1.2) { suggestions.push({ type: 'provider', target: provider, reason: `${provider} shows better historical performance`, expectedImprovement: `${((score / currentScore - 1) * 100).toFixed(0)}% better reliability`, }); } } } return suggestions; } async function analyzeErrorPattern( workflowId: string, error: Error, state: any, context: AgenticHookContext ): Promise<any> { // Analyze error to find patterns const pattern = { type: classifyError(error), confidence: 0.7, context: { step: state.currentStep, provider: state.provider, errorMessage: error.message, }, }; // Check for similar errors const errorHistory = await context.memory.cache.get( `errors:${workflowId}:${pattern.type}` ) || []; if (errorHistory.length > 5) { pattern.confidence = 0.9; pattern.context.recurring = true; pattern.context.occurrences = errorHistory.length; } return pattern; } async function findRecoveryStrategy( workflowId: string, error: Error, errorPattern: any, context: AgenticHookContext ): Promise<any | null> { // Find recovery strategy for error if (errorPattern.type === 'timeout') { return { type: 'retry', params: { maxRetries: 3, backoff: 'exponential', timeout: 30000, }, }; } if (errorPattern.type === 'rate_limit') { return { type: 'throttle', params: { delay: 1000, maxConcurrent: 1, }, }; } if (errorPattern.type === 'validation') { return { type: 'transform', params: { sanitize: true, validate: true, }, }; } return null; } function applyRecoveryStrategy(state: any, recovery: any): any { const recoveredState = { ...state }; switch (recovery.type) { case 'retry': recoveredState.retryConfig = recovery.params; recoveredState.shouldRetry = true; break; case 'throttle': recoveredState.throttleConfig = recovery.params; recoveredState.throttled = true; break; case 'transform': recoveredState.transformConfig = recovery.params; recoveredState.needsTransform = true; break; } recoveredState.recoveryApplied = recovery; delete recoveredState.error; // Clear error state return recoveredState; } function selectAlternativeProvider( currentProvider: string, providerStats: Map<string, { success: number; total: number }> ): string { // Select alternative provider based on stats let bestAlternative = 'anthropic'; // Default fallback let bestRate = 0; for (const [provider, stats] of providerStats) { if (provider === currentProvider) continue; const rate = stats.success / stats.total; if (rate > bestRate && stats.total >= 3) { bestRate = rate; bestAlternative = provider; } } return bestAlternative; } async function calculateAlternativeConfidence( workflowId: string, decisionPoint: string, option: string, state: any, context: AgenticHookContext ): Promise<number> { // Calculate confidence for alternative option const outcomeKey = `outcomes:${workflowId}:${decisionPoint}:${option}`; const outcomes = await context.memory.cache.get(outcomeKey) || []; if (outcomes.length === 0) { return 0.5; // Default confidence } const successRate = outcomes.filter((o: any) => o.success).length / outcomes.length; // Adjust for recency const recentOutcomes = outcomes.slice(-10); const recentSuccessRate = recentOutcomes.filter((o: any) => o.success).length / recentOutcomes.length; return successRate * 0.7 + recentSuccessRate * 0.3; } function extractDecisions(state: any): any[] { // Extract decisions from state return state.decisions || []; } function classifyError(error: Error): string { const message = error.message.toLowerCase(); if (message.includes('timeout')) return 'timeout'; if (message.includes('rate limit')) return 'rate_limit'; if (message.includes('validation')) return 'validation'; if (message.includes('network')) return 'network'; if (message.includes('auth')) return 'authentication'; return 'unknown'; } // ===== Register Hooks ===== export function registerWorkflowHooks(): void { agenticHookManager.register(workflowStartHook); agenticHookManager.register(workflowStepHook); agenticHookManager.register(workflowDecisionHook); agenticHookManager.register(workflowCompleteHook); agenticHookManager.register(workflowErrorHook); }