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); }