agent-ecosystem/src/shared/utils/teamLaunchFailureReason.ts

142 lines
4.5 KiB
TypeScript

import type {
MemberLaunchState,
MemberSpawnStatus,
TeamAgentRuntimeDiagnosticSeverity,
TeamAgentRuntimeLivenessKind,
} from '@shared/types';
export interface ProvisionedButNotAliveLaunchEntry {
launchState?: MemberLaunchState;
status?: MemberSpawnStatus;
hardFailure?: boolean;
hardFailureReason?: string;
error?: string;
runtimeDiagnostic?: string;
runtimeDiagnosticSeverity?: TeamAgentRuntimeDiagnosticSeverity;
bootstrapConfirmed?: boolean;
livenessKind?: TeamAgentRuntimeLivenessKind;
}
export function stripProcessTableUnavailableDiagnosticSuffix(reason: string): string | null {
const match = /^(.*?);\s*process table (?:is )?unavailable$/i.exec(reason.trim());
const baseReason = match?.[1]?.trim();
return baseReason && baseReason.length > 0 ? baseReason : null;
}
export function isProvisionedButNotAliveFailureReason(reason?: string): boolean {
return isCliProvisionedButNotAliveFailureReason(reason);
}
export function isCliProvisionedButNotAliveFailureReason(reason?: string): boolean {
const text = reason?.trim();
if (!text) {
return false;
}
const normalizedText = stripProcessTableUnavailableDiagnosticSuffix(text) ?? text;
return /^CLI process exited \(code (?:unknown|-?\d+|\?)\)\s+[-\u2013\u2014]\s+team provisioned but not alive$/i.test(
normalizedText
);
}
export function mentionsProcessTableUnavailable(value: string | undefined): boolean {
return /\bprocess table\b.*\bunavailable\b/i.test(value ?? '');
}
export function hasBootstrapConfirmationProofForLaunchFailure(
entry: ProvisionedButNotAliveLaunchEntry | undefined
): boolean {
return (
entry?.bootstrapConfirmed === true ||
entry?.launchState === 'confirmed_alive' ||
entry?.livenessKind === 'confirmed_bootstrap'
);
}
export function isProvisionedButNotAliveLaunchFailure(
entry: ProvisionedButNotAliveLaunchEntry | undefined
): boolean {
if (!entry) {
return false;
}
const hardFailureReason = entry.hardFailureReason?.trim();
const failureReasonMatches = hardFailureReason
? isProvisionedButNotAliveFailureReason(hardFailureReason)
: isProvisionedButNotAliveFailureReason(entry.error ?? entry.runtimeDiagnostic);
if (!failureReasonMatches) {
return false;
}
return (
entry.launchState === 'failed_to_start' ||
entry.status === 'error' ||
entry.hardFailure === true
);
}
export function isBootstrapConfirmedProvisionedButNotAliveFailure(
entry: ProvisionedButNotAliveLaunchEntry | undefined
): boolean {
return (
isProvisionedButNotAliveLaunchFailure(entry) &&
hasBootstrapConfirmationProofForLaunchFailure(entry)
);
}
export function hasUnsafeProvisionedButNotAliveRuntimeEvidence(
entry: ProvisionedButNotAliveLaunchEntry | undefined
): boolean {
if (!entry) {
return false;
}
if (entry.runtimeDiagnosticSeverity === 'error') {
return true;
}
if (
entry.livenessKind === 'not_found' ||
entry.livenessKind === 'shell_only' ||
entry.livenessKind === 'permission_blocked' ||
entry.livenessKind === 'runtime_process_candidate'
) {
return true;
}
const hasProcessTableUnavailableMarker =
mentionsProcessTableUnavailable(entry.runtimeDiagnostic) ||
mentionsProcessTableUnavailable(entry.hardFailureReason) ||
mentionsProcessTableUnavailable(entry.error);
if (!entry.livenessKind) {
return !hasProcessTableUnavailableMarker;
}
if (entry.livenessKind !== 'registered_only' && entry.livenessKind !== 'stale_metadata') {
return false;
}
return !hasProcessTableUnavailableMarker;
}
export function hasUnsafeProvisionedButNotAliveRuntimeEvidenceWithSpawnContext(
spawnEntry: ProvisionedButNotAliveLaunchEntry | undefined,
runtimeEntry: ProvisionedButNotAliveLaunchEntry | undefined
): boolean {
if (hasUnsafeProvisionedButNotAliveRuntimeEvidence(spawnEntry)) {
return true;
}
if (!runtimeEntry) {
return false;
}
const runtimeDiagnostic = runtimeEntry.runtimeDiagnostic?.trim();
if (
!runtimeDiagnostic &&
(runtimeEntry.livenessKind == null ||
runtimeEntry.livenessKind === 'registered_only' ||
runtimeEntry.livenessKind === 'stale_metadata')
) {
return hasUnsafeProvisionedButNotAliveRuntimeEvidence({
runtimeDiagnostic: spawnEntry?.runtimeDiagnostic,
hardFailureReason: spawnEntry?.hardFailureReason,
error: spawnEntry?.error,
runtimeDiagnosticSeverity: runtimeEntry.runtimeDiagnosticSeverity,
livenessKind: runtimeEntry.livenessKind,
});
}
return hasUnsafeProvisionedButNotAliveRuntimeEvidence(runtimeEntry);
}