/** * Agent Error Boundary Component * Specialized error boundary for Agent Audit pages with retry and recovery */ import { Component, ReactNode } from 'react'; import { AlertTriangle, RefreshCw, Terminal, ArrowLeft, Bug } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { cn } from '@/shared/utils/utils'; interface Props { children: ReactNode; taskId?: string; onRetry?: () => void; onReset?: () => void; maxRetries?: number; } interface State { hasError: boolean; error: Error | null; errorInfo: React.ErrorInfo | null; retryCount: number; isRetrying: boolean; } export class AgentErrorBoundary extends Component { private retryTimeoutId: ReturnType | null = null; constructor(props: Props) { super(props); this.state = { hasError: false, error: null, errorInfo: null, retryCount: 0, isRetrying: false, }; } static getDerivedStateFromError(error: Error): Partial { return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { console.error('[AgentErrorBoundary] Caught error:', error, errorInfo); this.setState({ errorInfo }); // Report error to monitoring (placeholder for actual implementation) this.reportError(error, errorInfo); } componentWillUnmount() { if (this.retryTimeoutId) { clearTimeout(this.retryTimeoutId); } } private reportError(error: Error, errorInfo: React.ErrorInfo) { // Structured error report const report = { timestamp: new Date().toISOString(), taskId: this.props.taskId, error: { name: error.name, message: error.message, stack: error.stack, }, componentStack: errorInfo.componentStack, userAgent: navigator.userAgent, url: window.location.href, }; // Log locally for development if (import.meta.env.DEV) { console.error('[AgentErrorBoundary] Error Report:', report); } // Future: send to error tracking service } private getErrorCategory(): 'network' | 'stream' | 'render' | 'unknown' { const message = this.state.error?.message?.toLowerCase() || ''; if (message.includes('fetch') || message.includes('network') || message.includes('connection')) { return 'network'; } if (message.includes('stream') || message.includes('sse') || message.includes('eventsource')) { return 'stream'; } if (message.includes('render') || message.includes('react') || message.includes('component')) { return 'render'; } return 'unknown'; } private getRecoveryHint(): string { const category = this.getErrorCategory(); switch (category) { case 'network': return 'Check your network connection and try again'; case 'stream': return 'The live connection was interrupted. Refresh to reconnect'; case 'render': return 'A display error occurred. Try refreshing the page'; default: return 'An unexpected error occurred'; } } handleRetry = async () => { const maxRetries = this.props.maxRetries ?? 3; if (this.state.retryCount >= maxRetries) { return; } this.setState({ isRetrying: true }); // Exponential backoff delay const delay = Math.min(1000 * Math.pow(2, this.state.retryCount), 10000); await new Promise(resolve => { this.retryTimeoutId = setTimeout(resolve, delay); }); this.setState(prev => ({ hasError: false, error: null, errorInfo: null, retryCount: prev.retryCount + 1, isRetrying: false, })); this.props.onRetry?.(); }; handleReset = () => { this.setState({ hasError: false, error: null, errorInfo: null, retryCount: 0, isRetrying: false, }); this.props.onReset?.(); }; handleGoBack = () => { window.history.back(); }; handleReload = () => { window.location.reload(); }; render() { const { hasError, error, errorInfo, retryCount, isRetrying } = this.state; const maxRetries = this.props.maxRetries ?? 3; const canRetry = retryCount < maxRetries; const category = this.getErrorCategory(); if (!hasError) { return this.props.children; } return (
{/* Error Header */}

Agent Error

{this.getRecoveryHint()}

{/* Error Details */}
Error Details
{error && (

{error.name}

{error.message}

)} {this.props.taskId && (
Task ID: {this.props.taskId}
)} {retryCount > 0 && (
Retry attempts: {retryCount}/{maxRetries}
)} {/* Stack trace (dev only) */} {import.meta.env.DEV && error?.stack && (
Stack Trace
                    {error.stack}
                  
)} {import.meta.env.DEV && errorInfo?.componentStack && (
Component Stack
                    {errorInfo.componentStack}
                  
)}
{/* Actions */}
{canRetry && ( )}
{/* Recovery suggestion */} {!canRetry && (

Maximum retry attempts reached. Please refresh the page or contact support.

)}
); } } export default AgentErrorBoundary;