agent-ecosystem/src/main/services/team/provisioning/TeamProvisioningLaunchDiagnostics.ts

197 lines
5.9 KiB
TypeScript

import type { WorkspaceTrustExecutionResult } from '@features/workspace-trust/main';
import type { MemberSpawnStatusEntry, TeamLaunchDiagnosticItem } from '@shared/types';
export interface TeamProvisioningLaunchDiagnosticsRun {
isLaunch: boolean;
memberSpawnStatuses?: ReadonlyMap<string, MemberSpawnStatusEntry> | null;
}
interface LaunchDiagnosticsClockOptions {
nowIso?: () => string;
}
const defaultNowIso = (): string => new Date().toISOString();
export function mentionsProcessTableUnavailable(value: string | undefined): boolean {
return /\bprocess table\b.*\bunavailable\b/i.test(value ?? '');
}
export function buildLaunchDiagnosticsFromRun(
run: TeamProvisioningLaunchDiagnosticsRun,
options: LaunchDiagnosticsClockOptions = {}
): TeamLaunchDiagnosticItem[] | undefined {
const memberSpawnStatuses = run.memberSpawnStatuses;
if (!run.isLaunch || !memberSpawnStatuses || memberSpawnStatuses.size === 0) {
return undefined;
}
const observedAt = (options.nowIso ?? defaultNowIso)();
const items: TeamLaunchDiagnosticItem[] = [];
for (const [memberName, entry] of memberSpawnStatuses.entries()) {
if (entry.launchState === 'confirmed_alive') {
items.push({
id: `${memberName}:bootstrap_confirmed`,
memberName,
severity: 'info',
code: 'bootstrap_confirmed',
label: `${memberName} - bootstrap confirmed`,
observedAt,
});
continue;
}
if (entry.launchState === 'failed_to_start') {
items.push({
id: `${memberName}:bootstrap_stalled`,
memberName,
severity: 'error',
code: 'bootstrap_stalled',
label: `${memberName} - failed to start`,
detail: entry.hardFailureReason ?? entry.error,
observedAt,
});
continue;
}
if (entry.launchState === 'runtime_pending_permission') {
items.push({
id: `${memberName}:permission_pending`,
memberName,
severity: 'warning',
code: 'permission_pending',
label: `${memberName} - awaiting permission`,
detail: entry.runtimeDiagnostic,
observedAt,
});
continue;
}
if (entry.bootstrapStalled === true) {
items.push({
id: `${memberName}:bootstrap_stalled`,
memberName,
severity: 'warning',
code: 'bootstrap_stalled',
label: `${memberName} - bootstrap stalled`,
detail: entry.runtimeDiagnostic,
observedAt,
});
continue;
}
if (mentionsProcessTableUnavailable(entry.runtimeDiagnostic)) {
items.push({
id: `${memberName}:process_table_unavailable`,
memberName,
severity: 'warning',
code: 'process_table_unavailable',
label: `${memberName} - process table unavailable`,
detail: entry.runtimeDiagnostic,
observedAt,
});
continue;
}
if (entry.livenessKind === 'shell_only') {
items.push({
id: `${memberName}:tmux_shell_only`,
memberName,
severity: 'warning',
code: 'tmux_shell_only',
label: `${memberName} - shell only`,
detail: entry.runtimeDiagnostic,
observedAt,
});
continue;
}
if (entry.livenessKind === 'runtime_process_candidate') {
items.push({
id: `${memberName}:runtime_process_candidate`,
memberName,
severity: 'warning',
code: 'runtime_process_candidate',
label: `${memberName} - bootstrap unconfirmed`,
detail: entry.runtimeDiagnostic,
observedAt,
});
continue;
}
if (entry.livenessKind === 'runtime_process') {
items.push({
id: `${memberName}:runtime_process_detected`,
memberName,
severity: 'info',
code: 'runtime_process_detected',
label: `${memberName} - waiting for bootstrap`,
detail: entry.runtimeDiagnostic,
observedAt,
});
continue;
}
if (
entry.livenessKind === 'registered_only' ||
entry.livenessKind === 'stale_metadata' ||
entry.livenessKind === 'not_found'
) {
items.push({
id: `${memberName}:runtime_not_found`,
memberName,
severity: 'warning',
code: 'runtime_not_found',
label: `${memberName} - waiting for runtime`,
detail: entry.runtimeDiagnostic,
observedAt,
});
continue;
}
if (entry.agentToolAccepted) {
items.push({
id: `${memberName}:spawn_accepted`,
memberName,
severity: 'info',
code: 'spawn_accepted',
label: `${memberName} - spawn accepted`,
detail: entry.runtimeDiagnostic,
observedAt,
});
}
}
return items.length > 0 ? items : undefined;
}
export function buildWorkspaceTrustPreflightLaunchDiagnostic(
execution: WorkspaceTrustExecutionResult,
options: LaunchDiagnosticsClockOptions = {}
): TeamLaunchDiagnosticItem | null {
if (execution.status === 'cancelled') {
return null;
}
const severity =
execution.status === 'blocked'
? 'error'
: execution.status === 'soft_failed'
? 'warning'
: 'info';
const label =
execution.status === 'blocked'
? 'Workspace trust preflight blocked launch'
: execution.status === 'soft_failed'
? 'Workspace trust preflight could not verify trust'
: 'Workspace trust preflight completed';
const detail =
execution.errorMessage?.trim() ||
execution.errorCode?.trim() ||
execution.evidence?.find((item) => item.trim().length > 0)?.trim();
return {
id: 'workspace-trust:preflight',
severity,
code: 'workspace_trust_preflight',
label,
...(detail ? { detail } : {}),
observedAt: (options.nowIso ?? defaultNowIso)(),
};
}
export function mergeLaunchDiagnosticItem(
items: readonly TeamLaunchDiagnosticItem[] | undefined,
item: TeamLaunchDiagnosticItem
): TeamLaunchDiagnosticItem[] {
return [...(items ?? []).filter((candidate) => candidate.id !== item.id), item];
}