Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | 16x 35x 35x 19x 5x 5x 14x 2x 2x 2x 2x 2x 2x 4x 4x 4x 2x 2x 2x 2x 5x 5x 5x 5x | /**
* RetryPolicy — Comprehensive retry logic with error-specific handling.
*
* Implements intelligent retry strategies for different types of agent failures.
* Replaces scattered retry logic with unified, configurable policies.
*/
import { createModuleLogger } from '../../logger/index.js';
const log = createModuleLogger('retry-policy');
export type AgentErrorType =
| 'auth_failure' // 401 errors, invalid tokens
| 'usage_limit' // Rate limiting, quota exceeded
| 'missing_signal' // Process completed but no signal.json
| 'process_crash' // Process exited with error code
| 'timeout' // Process timed out
| 'unknown'; // Unclassified errors
export interface AgentError {
type: AgentErrorType;
message: string;
isTransient: boolean; // Can this error be resolved by retrying?
requiresAccountSwitch: boolean; // Should we switch to next account?
shouldPersistToDB: boolean; // Should this error be saved for debugging?
exitCode?: number | null;
signal?: string | null;
originalError?: Error;
}
export interface RetryPolicy {
readonly maxAttempts: number;
readonly backoffMs: number[];
shouldRetry(error: AgentError, attempt: number): boolean;
getRetryDelay(attempt: number): number;
}
export class DefaultRetryPolicy implements RetryPolicy {
readonly maxAttempts = 3;
readonly backoffMs = [1000, 2000, 4000]; // 1s, 2s, 4s exponential backoff
shouldRetry(error: AgentError, attempt: number): boolean {
if (attempt >= this.maxAttempts) {
log.debug({
errorType: error.type,
attempt,
maxAttempts: this.maxAttempts
}, 'max retry attempts reached');
return false;
}
switch (error.type) {
case 'auth_failure':
// Retry auth failures - tokens might be refreshed
log.debug({ attempt, errorType: error.type }, 'retrying auth failure');
return true;
case 'usage_limit':
// Don't retry usage limits - need account switch
log.debug({ attempt, errorType: error.type }, 'not retrying usage limit - requires account switch');
return false;
case 'missing_signal':
// Retry missing signal - add instruction prompt
log.debug({ attempt, errorType: error.type }, 'retrying missing signal with instruction');
return true;
case 'process_crash':
// Only retry transient crashes
const shouldRetryTransient = error.isTransient;
log.debug({
attempt,
errorType: error.type,
isTransient: error.isTransient,
shouldRetry: shouldRetryTransient
}, 'process crash retry decision');
return shouldRetryTransient;
case 'timeout':
// Retry timeouts up to max attempts
log.debug({ attempt, errorType: error.type }, 'retrying timeout');
return true;
case 'unknown':
default:
// Don't retry unknown errors by default
log.debug({ attempt, errorType: error.type }, 'not retrying unknown error');
return false;
}
}
getRetryDelay(attempt: number): number {
const index = Math.min(attempt - 1, this.backoffMs.length - 1);
const delay = this.backoffMs[index] || this.backoffMs[this.backoffMs.length - 1];
log.debug({ attempt, delay }, 'retry delay calculated');
return delay;
}
}
/**
* AgentExhaustedError - Special error indicating account needs switching.
* When thrown, caller should attempt account failover rather than retry.
*/
export class AgentExhaustedError extends Error {
constructor(message: string, public readonly originalError?: AgentError) {
super(message);
this.name = 'AgentExhaustedError';
}
}
/**
* AgentFailureError - Terminal failure that cannot be retried.
* Indicates all retry attempts have been exhausted or error is non-retriable.
*/
export class AgentFailureError extends Error {
constructor(message: string, public readonly originalError?: AgentError) {
super(message);
this.name = 'AgentFailureError';
}
} |