agent-ecosystem/src/renderer/utils/memberLaunchDiagnostics.ts
2026-04-28 16:08:05 +03:00

189 lines
6.7 KiB
TypeScript

import type {
MemberLaunchState,
MemberSpawnLivenessSource,
MemberSpawnStatus,
MemberSpawnStatusEntry,
TeamAgentRuntimeDiagnosticSeverity,
TeamAgentRuntimeEntry,
TeamAgentRuntimeLivenessKind,
TeamAgentRuntimePidSource,
} from '@shared/types';
export interface MemberLaunchDiagnosticsPayload {
teamName?: string;
runId?: string;
memberName: string;
launchState?: MemberLaunchState;
spawnStatus?: MemberSpawnStatus;
livenessKind?: TeamAgentRuntimeLivenessKind;
livenessSource?: MemberSpawnLivenessSource;
pid?: number;
pidSource?: TeamAgentRuntimePidSource;
paneId?: string;
panePid?: number;
paneCurrentCommand?: string;
processCommand?: string;
runtimePid?: number;
runtimeSessionId?: string;
runtimeDiagnostic?: string;
runtimeDiagnosticSeverity?: TeamAgentRuntimeDiagnosticSeverity;
diagnostics?: string[];
updatedAt?: string;
}
const MAX_DIAGNOSTIC_STRING_LENGTH = 500;
const MAX_DIAGNOSTIC_ITEMS = 20;
const SECRET_FLAG_PATTERN =
/(--(?:api-key|token|password|secret|authorization|auth-token)(?:=|\s+))("[^"]*"|'[^']*'|\S+)/gi;
function boundedString(
value: string | undefined,
maxLength = MAX_DIAGNOSTIC_STRING_LENGTH
): string | undefined {
const trimmed = value?.replace(/\s+/g, ' ').trim();
if (!trimmed) return undefined;
const redacted = trimmed.replace(SECRET_FLAG_PATTERN, '$1[redacted]');
return redacted.length > maxLength
? `${redacted.slice(0, Math.max(0, maxLength - 3))}...`
: redacted;
}
function boundedNumber(value: number | undefined): number | undefined {
return typeof value === 'number' && Number.isFinite(value) && value > 0
? Math.trunc(value)
: undefined;
}
function uniqueDiagnostics(
...groups: (readonly (string | undefined)[] | undefined)[]
): string[] | undefined {
const seen = new Set<string>();
const diagnostics: string[] = [];
for (const group of groups) {
for (const item of group ?? []) {
const normalized = boundedString(item);
if (!normalized || seen.has(normalized)) continue;
seen.add(normalized);
diagnostics.push(normalized);
if (diagnostics.length >= MAX_DIAGNOSTIC_ITEMS) {
return diagnostics;
}
}
}
return diagnostics.length > 0 ? diagnostics : undefined;
}
export function buildMemberLaunchDiagnosticsPayload(params: {
teamName?: string | null;
runId?: string | null;
memberName: string;
spawnStatus?: MemberSpawnStatus;
launchState?: MemberLaunchState;
livenessSource?: MemberSpawnLivenessSource;
spawnEntry?: MemberSpawnStatusEntry;
runtimeEntry?: TeamAgentRuntimeEntry;
}): MemberLaunchDiagnosticsPayload {
const spawnEntry = params.spawnEntry;
const runtimeEntry = params.runtimeEntry;
const runtimeDiagnostic =
boundedString(spawnEntry?.runtimeDiagnostic) ??
boundedString(runtimeEntry?.runtimeDiagnostic) ??
boundedString(spawnEntry?.hardFailureReason) ??
boundedString(spawnEntry?.error);
const diagnostics = uniqueDiagnostics(
runtimeDiagnostic ? [runtimeDiagnostic] : undefined,
spawnEntry?.hardFailureReason ? [spawnEntry.hardFailureReason] : undefined,
spawnEntry?.error ? [spawnEntry.error] : undefined,
runtimeEntry?.diagnostics
);
const runId = boundedString(params.runId ?? undefined);
return {
...(params.teamName ? { teamName: params.teamName } : {}),
...(runId ? { runId } : {}),
memberName: params.memberName,
...((spawnEntry?.launchState ?? params.launchState)
? { launchState: spawnEntry?.launchState ?? params.launchState }
: {}),
...((spawnEntry?.status ?? params.spawnStatus)
? { spawnStatus: spawnEntry?.status ?? params.spawnStatus }
: {}),
...((spawnEntry?.livenessKind ?? runtimeEntry?.livenessKind)
? { livenessKind: spawnEntry?.livenessKind ?? runtimeEntry?.livenessKind }
: {}),
...((spawnEntry?.livenessSource ?? params.livenessSource)
? { livenessSource: spawnEntry?.livenessSource ?? params.livenessSource }
: {}),
...(boundedNumber(runtimeEntry?.pid) ? { pid: boundedNumber(runtimeEntry?.pid) } : {}),
...(runtimeEntry?.pidSource ? { pidSource: runtimeEntry.pidSource } : {}),
...(boundedString(runtimeEntry?.paneId) ? { paneId: boundedString(runtimeEntry?.paneId) } : {}),
...(boundedNumber(runtimeEntry?.panePid)
? { panePid: boundedNumber(runtimeEntry?.panePid) }
: {}),
...(boundedString(runtimeEntry?.paneCurrentCommand)
? { paneCurrentCommand: boundedString(runtimeEntry?.paneCurrentCommand) }
: {}),
...(boundedString(runtimeEntry?.processCommand)
? { processCommand: boundedString(runtimeEntry?.processCommand) }
: {}),
...(boundedNumber(runtimeEntry?.runtimePid)
? { runtimePid: boundedNumber(runtimeEntry?.runtimePid) }
: {}),
...(boundedString(runtimeEntry?.runtimeSessionId)
? { runtimeSessionId: boundedString(runtimeEntry?.runtimeSessionId) }
: {}),
...(runtimeDiagnostic ? { runtimeDiagnostic } : {}),
...((spawnEntry?.runtimeDiagnosticSeverity ?? runtimeEntry?.runtimeDiagnosticSeverity)
? {
runtimeDiagnosticSeverity:
spawnEntry?.runtimeDiagnosticSeverity ?? runtimeEntry?.runtimeDiagnosticSeverity,
}
: {}),
...(diagnostics ? { diagnostics } : {}),
...(boundedString(spawnEntry?.updatedAt ?? runtimeEntry?.updatedAt)
? { updatedAt: boundedString(spawnEntry?.updatedAt ?? runtimeEntry?.updatedAt) }
: {}),
};
}
export function hasMemberLaunchDiagnosticsDetails(
payload: MemberLaunchDiagnosticsPayload
): boolean {
const weakLiveness =
payload.livenessKind === 'runtime_process_candidate' ||
payload.livenessKind === 'permission_blocked' ||
payload.livenessKind === 'shell_only' ||
payload.livenessKind === 'registered_only' ||
payload.livenessKind === 'stale_metadata' ||
payload.livenessKind === 'not_found';
return Boolean(
(payload.launchState && payload.launchState !== 'confirmed_alive') ||
(payload.spawnStatus && payload.spawnStatus !== 'online') ||
weakLiveness ||
payload.runtimeDiagnostic ||
payload.diagnostics?.length
);
}
export function hasMemberLaunchDiagnosticsError(payload: MemberLaunchDiagnosticsPayload): boolean {
return Boolean(
payload.spawnStatus === 'error' ||
payload.launchState === 'failed_to_start' ||
payload.runtimeDiagnosticSeverity === 'error'
);
}
export function getMemberLaunchDiagnosticsErrorMessage(
payload: MemberLaunchDiagnosticsPayload
): string | undefined {
if (!hasMemberLaunchDiagnosticsError(payload)) {
return undefined;
}
return payload.runtimeDiagnostic ?? payload.diagnostics?.[0] ?? 'Launch failed';
}
export function formatMemberLaunchDiagnosticsPayload(
payload: MemberLaunchDiagnosticsPayload
): string {
return JSON.stringify(payload, null, 2);
}