Files
PaperClipAI/tests/runner-e2e/failure-classifier.ts
T
DottaandPaperclip e2b89b6302 Integrate pinned Cursor ACP runtime and explicit public setup
Consolidate Cursor v10 onto current controller behavior, retain admission gates, and prepare native interaction and cleanup qualification. Add the public provisioner and ordinary provider-pack assets.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-03 13:33:15 -05:00

59 lines
3.1 KiB
TypeScript

import { ObservedStateTimeout, RemoteAdmissionReadError } from "./api.js";
import type { FailureClass } from "./types.js";
const TRANSIENT =
/(?:\b429\b|\b5\d\d\b|rate.?limit|ECONN(?:RESET|REFUSED)|socket hang up|network (?:error|interruption|timeout)|service unavailable|(?:provider|server|bootstrap|browser|webserver|health|daytona|sandbox|ingress|preview|connection|harness).*(?:temporar|timed? out|timeout|closed|failed|unavailable|interrupt|reset|refused|create|start|connect)|(?:timed? out|timeout).*(?:provider|server|bootstrap|browser|webserver|health|daytona|sandbox|ingress|preview|connection|harness))/i;
const PERMANENT =
/(?:missing (?:credential|fixture secret)|invalid.*(?:credential|api key)|unauthorized|forbidden|qualification|model.*(?:unsupported|incompatible)|artifact.*incompatible|runner_remote_.*(?:incompatible|unavailable)|immutable image digest)/i;
// These controller checkpoint failures are reproducible protocol defects, even
// when their outer error is described as a provider transport failure.
const NON_RETRYABLE_SESSION_CLOSE =
/(?:native_session_close_unrecoverable|runner did not durably suspend before checkpoint)/i;
// Recovery cannot repair a provider session-open rejection that explicitly
// declares itself non-retryable. Keep generic transport/start failures transient.
const NON_RETRYABLE_ACPX_SESSION_OPEN =
/native_session_recovery_failed[\s\S]*ACPX sidecar command session\.open was rejected\s*\([^)]*\bretryable\s*=\s*false\b/i;
const CANDIDATE =
/(?:matcher|expected.*observed|marker|issue status|run status|runtime mode|wrong output|missing output)/i;
// File-transfer deadlines are sandbox transport failures. Do not generalize
// this to all RPC timeouts: runner protocol defects must remain visible.
const SANDBOX_TRANSFER_TIMEOUT =
/RPC call "environmentSync(?:In|Out)" timed out after \d+ms/i;
export function classifyFailure(error: unknown): FailureClass {
if (error instanceof ObservedStateTimeout || error instanceof RemoteAdmissionReadError) return error.failureClass;
const message =
error instanceof Error ? `${error.name}: ${error.message}` : String(error);
if (/browser bootstrap failed before task creation/i.test(message))
return "transient_infrastructure";
if (/secret.*(?:leak|plaintext|redaction)/i.test(message))
return "secret_leak";
if (/cleanup|teardown|lease.*release/i.test(message)) {
return TRANSIENT.test(message)
? "transient_infrastructure"
: "cleanup_failure";
}
if (PERMANENT.test(message)) return "permanent_infrastructure";
if (
/chat_idle_state_invariant/.test(message) ||
NON_RETRYABLE_SESSION_CLOSE.test(message) ||
NON_RETRYABLE_ACPX_SESSION_OPEN.test(message)
)
return "candidate_failure";
if (TRANSIENT.test(message) || SANDBOX_TRANSFER_TIMEOUT.test(message))
return "transient_infrastructure";
if (CANDIDATE.test(message)) return "candidate_failure";
return "candidate_failure";
}
export function shouldRetryFailure(
failureClass: FailureClass,
maxAutomaticRetries = 1,
) {
return (
maxAutomaticRetries > 0 &&
(failureClass === "transient_infrastructure" ||
failureClass === "provider_variance")
);
}