mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-02 08:28:48 +01:00
Failed lifecycle verification could allocate another test environment every minute indefinitely. Count failed host verification tests separately from claims, persist a repair wait for confirmed configuration failures or three generic failed tests, and clear it through authorized retry or execution configuration changes. Preserve typed temporary execution-target failures through adapter checks so timeouts, transport failures and interrupted probes keep automatic recovery. Retain existing readiness, authorization, termination and cleanup gates. Document the policy and add real PostgreSQL persistence and actual OpenCode adapter regressions. Verified 398 focused tests (one Windows-only test skipped on macOS), repository-wide typecheck and build, structural checks, fully green current-head CI and Greptile 5/5 with no unresolved comments. Co-Authored-By: Paperclip <noreply@paperclip.ing>
52 lines
3.7 KiB
TypeScript
52 lines
3.7 KiB
TypeScript
import type { LifecycleFailure } from "./ports.js";
|
|
|
|
// Only fixed text may leave this boundary. Provider errors can contain secrets.
|
|
const reasons = {
|
|
harness_test_failed: "The execution test did not pass. Open Configuration and run the agent test to inspect the failed checks.",
|
|
harness_auth_failed: "Authentication failed. Check the agent's AI connection or sign in to its execution tool, then retry.",
|
|
harness_model_unavailable: "The selected model is unavailable. Choose a model supported by the agent's AI connection in Configuration, then retry.",
|
|
harness_command_unavailable: "The execution command was not found. Check the command and its installation in the agent's environment, then retry.",
|
|
harness_cwd_invalid: "The working directory is unavailable. Check the working directory in Configuration, then retry.",
|
|
harness_probe_timed_out: "The execution test timed out. Paperclip will retry automatically. If this continues, run the agent test in Configuration.",
|
|
harness_probe_unexpected_output: "The execution test did not return the expected response. Run the agent test in Configuration to inspect the failed checks.",
|
|
test_environment_unavailable: "The test environment is unavailable. Check the agent's environment in Configuration. Paperclip will retry automatically.",
|
|
required_plugin_unavailable: "A required plugin is unavailable. Check the plugin's status in Settings. Paperclip will retry automatically.",
|
|
invalid_lifecycle_result: "A required plugin returned an invalid result. Check the plugin's status in Settings. Paperclip will retry automatically.",
|
|
} as const;
|
|
|
|
const transientCodes = new Set(["ECONNREFUSED", "ECONNRESET", "ETIMEDOUT", "40001", "40P01", "53300", "57P01"]);
|
|
const configurationFailures = new Set([
|
|
"harness_auth_failed", "harness_model_unavailable",
|
|
"harness_command_unavailable", "harness_cwd_invalid", "harness_probe_unexpected_output",
|
|
]);
|
|
|
|
export function lifecycleVerificationNeedsRetry(context: LifecycleFailure, error: unknown): boolean {
|
|
const code = lifecycleFailureCode(error) ?? "";
|
|
return context.phase === "verifying" && context.stage === "host"
|
|
&& (configurationFailures.has(code) || (code === "harness_test_failed" && (context.verificationFailures ?? 0) >= 3));
|
|
}
|
|
|
|
export function lifecycleFailureCode(error: unknown): string | undefined {
|
|
const candidate = error as { code?: unknown; cause?: { code?: unknown } } | null;
|
|
return [candidate?.code, candidate?.cause?.code].find((code): code is string =>
|
|
typeof code === "string" && (Object.hasOwn(reasons, code) || transientCodes.has(code)));
|
|
}
|
|
|
|
export function lifecycleFailureMessage(context: LifecycleFailure, error: unknown): string {
|
|
const phases = {
|
|
preparing: "Agent preparation", verifying: "Execution configuration check", pausing: "Stopping agent execution",
|
|
resuming: "Starting agent resources", terminating: "Stopping agent execution", cleaning_up: "Agent resource cleanup",
|
|
};
|
|
const phase = context.phase && Object.hasOwn(phases, context.phase)
|
|
? phases[context.phase as keyof typeof phases] : "Agent setup";
|
|
const step = context.stage === "plugin" ? `${phase}: required plugin step`
|
|
: context.stage === "select_plugins" ? `${phase}: finding required plugins`
|
|
: ["save_plugins", "complete_host", "complete_plugin", "transition"].includes(context.stage)
|
|
? `${phase}: saving progress` : phase;
|
|
const code = lifecycleFailureCode(error);
|
|
const reason = code && Object.hasOwn(reasons, code) ? reasons[code as keyof typeof reasons]
|
|
: "Paperclip could not complete this step. It will retry automatically. If this continues, contact your instance administrator.";
|
|
return `${step} failed. ${reason}${lifecycleVerificationNeedsRetry(context, error)
|
|
? " Automatic verification is paused. Update Configuration or choose Retry now." : ""}`;
|
|
}
|