import type { MemberSpawnStatusEntry, TeamAgentRuntimeDiagnosticSeverity, TeamAgentRuntimeEntry, TeamAgentRuntimeSnapshot, TeamProviderBackendId, TeamProviderId, } from '@shared/types'; export type RuntimeDisplayState = | 'running' | 'starting' | 'waiting' | 'degraded' | 'stopped' | 'unknown'; export interface TeamRuntimeDisplayMember { name: string; } export interface TeamRuntimeDisplayRow { memberName: string; state: RuntimeDisplayState; stateReason: string; source: 'runtime' | 'spawn-status' | 'mixed'; updatedAt?: string; providerId?: TeamProviderId; providerBackendId?: TeamProviderBackendId; laneId?: string; laneKind?: 'primary' | 'secondary'; runtimeModel?: string; diagnostic?: string; diagnosticSeverity?: TeamAgentRuntimeDiagnosticSeverity; pidLabel?: string; actionsAllowed: false; } interface SpawnDegradation { reason: string; diagnostic?: string; diagnosticSeverity: TeamAgentRuntimeDiagnosticSeverity; } const ACTIVE_SPAWN_STATUSES = new Set(['waiting', 'spawning']); export function buildTeamRuntimeDisplayRows({ members, runtimeSnapshot, spawnStatuses, }: { members: readonly TeamRuntimeDisplayMember[]; runtimeSnapshot?: TeamAgentRuntimeSnapshot | null; spawnStatuses?: Record | null; }): TeamRuntimeDisplayRow[] { const runtimeByMember = buildRuntimeEntriesByMember(runtimeSnapshot); return members.map((member) => { const runtime = pickLatestRuntimeEntry(runtimeByMember.get(member.name) ?? []); const spawn = spawnStatuses?.[member.name]; return buildRuntimeDisplayRow(member.name, runtime, spawn); }); } function buildRuntimeEntriesByMember( runtimeSnapshot?: TeamAgentRuntimeSnapshot | null ): Map { const byMember = new Map(); const runtimeMembers = runtimeSnapshot?.members; if (!runtimeMembers) return byMember; for (const [key, entry] of Object.entries(runtimeMembers)) { const memberName = entry.memberName || key; if (!memberName) continue; const entries = byMember.get(memberName); if (entries) { entries.push(entry); } else { byMember.set(memberName, [entry]); } } return byMember; } function pickLatestRuntimeEntry( entries: readonly TeamAgentRuntimeEntry[] ): TeamAgentRuntimeEntry | undefined { let latest: TeamAgentRuntimeEntry | undefined; let latestTimestamp = Number.NEGATIVE_INFINITY; for (const entry of entries) { const timestamp = getRuntimeEntryTimestamp(entry); if (!latest || timestamp >= latestTimestamp) { latest = entry; latestTimestamp = timestamp; } } return latest; } function getRuntimeEntryTimestamp(entry: TeamAgentRuntimeEntry): number { const timestamp = Date.parse(entry.runtimeLastSeenAt ?? entry.updatedAt ?? ''); return Number.isFinite(timestamp) ? timestamp : 0; } function buildRuntimeDisplayRow( memberName: string, runtime?: TeamAgentRuntimeEntry, spawn?: MemberSpawnStatusEntry ): TeamRuntimeDisplayRow { if (runtime) { return buildRuntimeBackedDisplayRow(memberName, runtime, spawn); } if (spawn) { return buildSpawnBackedDisplayRow(memberName, spawn); } return { memberName, state: 'unknown', stateReason: 'No live runtime snapshot yet', source: 'spawn-status', actionsAllowed: false, }; } function buildRuntimeBackedDisplayRow( memberName: string, runtime: TeamAgentRuntimeEntry, spawn?: MemberSpawnStatusEntry ): TeamRuntimeDisplayRow { const hasErrorDiagnostic = runtime.runtimeDiagnosticSeverity === 'error'; const spawnDegradation = getSpawnDegradation(spawn); const state = getRuntimeBackedState(runtime, hasErrorDiagnostic, spawnDegradation != null); const degradedReason = spawnDegradation ? withLiveProcessContext(spawnDegradation.reason, runtime) : undefined; const stateReason = degradedReason ?? runtime.runtimeDiagnostic ?? (runtime.alive === true ? 'Runtime heartbeat is alive' : 'Runtime heartbeat is not alive'); return { memberName, state, stateReason, source: spawn ? 'mixed' : 'runtime', updatedAt: runtime.runtimeLastSeenAt ?? runtime.updatedAt, providerId: runtime.providerId, providerBackendId: runtime.providerBackendId, laneId: runtime.laneId, laneKind: runtime.laneKind, runtimeModel: runtime.runtimeModel, diagnostic: spawnDegradation && degradedReason ? withLiveProcessContext(spawnDegradation.diagnostic ?? degradedReason, runtime) : runtime.runtimeDiagnostic, diagnosticSeverity: spawnDegradation?.diagnosticSeverity ?? runtime.runtimeDiagnosticSeverity, pidLabel: formatRuntimePidLabel(runtime), actionsAllowed: false, }; } function getSpawnDegradation(spawn?: MemberSpawnStatusEntry): SpawnDegradation | null { if (!spawn) return null; if (spawn.status === 'error' || spawn.hardFailure === true) { const reason = spawn.error ?? spawn.hardFailureReason ?? spawn.runtimeDiagnostic ?? 'Spawn failed'; return { reason, diagnostic: spawn.runtimeDiagnostic ?? reason, diagnosticSeverity: spawn.runtimeDiagnosticSeverity ?? 'error', }; } if (spawn.bootstrapStalled === true) { const reason = spawn.runtimeDiagnostic ?? 'Runtime is alive, but bootstrap did not confirm'; return { reason, diagnostic: spawn.runtimeDiagnostic ?? reason, diagnosticSeverity: spawn.runtimeDiagnosticSeverity ?? 'warning', }; } if ((spawn.pendingPermissionRequestIds?.length ?? 0) > 0) { const reason = spawn.runtimeDiagnostic ?? 'Runtime is waiting for permission approval'; return { reason, diagnostic: spawn.runtimeDiagnostic ?? reason, diagnosticSeverity: spawn.runtimeDiagnosticSeverity ?? 'warning', }; } if (spawn.runtimeDiagnosticSeverity === 'error') { const reason = spawn.runtimeDiagnostic ?? 'Runtime launch status needs attention'; return { reason, diagnostic: spawn.runtimeDiagnostic, diagnosticSeverity: 'error', }; } return null; } function getRuntimeBackedState( runtime: TeamAgentRuntimeEntry, hasErrorDiagnostic: boolean, hasSpawnDegradation: boolean ): RuntimeDisplayState { if (hasSpawnDegradation || hasErrorDiagnostic) { return 'degraded'; } return runtime.alive === true ? 'running' : 'stopped'; } function withLiveProcessContext(reason: string, runtime: TeamAgentRuntimeEntry): string { if (runtime.alive !== true || /process is still alive/i.test(reason)) { return reason; } return `${reason}. Process is still alive.`; } function buildSpawnBackedDisplayRow( memberName: string, spawn: MemberSpawnStatusEntry ): TeamRuntimeDisplayRow { const spawnDegradation = getSpawnDegradation(spawn); if (spawnDegradation) { return { memberName, state: 'degraded', stateReason: spawnDegradation.reason, source: 'spawn-status', updatedAt: spawn.livenessLastCheckedAt ?? spawn.updatedAt, runtimeModel: spawn.runtimeModel, diagnostic: spawnDegradation.diagnostic, diagnosticSeverity: spawnDegradation.diagnosticSeverity, actionsAllowed: false, }; } if ( (spawn.status === 'online' && hasConfirmedSpawnLiveness(spawn)) || isConfirmedSpawnLaunch(spawn) ) { return { memberName, state: 'running', stateReason: spawn.runtimeDiagnostic ?? 'Bootstrap confirmed', source: 'spawn-status', updatedAt: spawn.livenessLastCheckedAt ?? spawn.lastHeartbeatAt ?? spawn.updatedAt, runtimeModel: spawn.runtimeModel, diagnostic: spawn.runtimeDiagnostic, diagnosticSeverity: spawn.runtimeDiagnosticSeverity, actionsAllowed: false, }; } if (ACTIVE_SPAWN_STATUSES.has(spawn.status)) { return { memberName, state: spawn.status === 'waiting' ? 'waiting' : 'starting', stateReason: spawn.runtimeDiagnostic ?? `Spawn status is ${spawn.status}`, source: 'spawn-status', updatedAt: spawn.livenessLastCheckedAt ?? spawn.updatedAt, runtimeModel: spawn.runtimeModel, diagnostic: spawn.runtimeDiagnostic, diagnosticSeverity: spawn.runtimeDiagnosticSeverity, actionsAllowed: false, }; } if (spawn.status === 'offline' || spawn.status === 'skipped') { return { memberName, state: 'stopped', stateReason: spawn.status === 'skipped' ? (spawn.skipReason ?? 'Member was skipped for launch') : 'Spawn status is offline', source: 'spawn-status', updatedAt: spawn.updatedAt, runtimeModel: spawn.runtimeModel, diagnostic: spawn.runtimeDiagnostic, diagnosticSeverity: spawn.runtimeDiagnosticSeverity, actionsAllowed: false, }; } return { memberName, state: 'unknown', stateReason: `Spawn status is ${String(spawn.status)}`, source: 'spawn-status', updatedAt: spawn.updatedAt, runtimeModel: spawn.runtimeModel, diagnostic: spawn.runtimeDiagnostic, diagnosticSeverity: spawn.runtimeDiagnosticSeverity, actionsAllowed: false, }; } function hasConfirmedSpawnLiveness(spawn: MemberSpawnStatusEntry): boolean { return ( spawn.runtimeAlive === true || spawn.bootstrapConfirmed === true || spawn.livenessSource === 'heartbeat' || spawn.livenessSource === 'process' ); } function isConfirmedSpawnLaunch(spawn: MemberSpawnStatusEntry): boolean { return spawn.launchState === 'confirmed_alive' && spawn.bootstrapConfirmed === true; } function formatRuntimePidLabel(runtime: TeamAgentRuntimeEntry): string | undefined { const runtimePid = getFinitePid(runtime.runtimePid); if (runtimePid != null) return `runtime pid ${runtimePid}`; const processPid = getFinitePid(runtime.pid); if (processPid != null) return `${runtime.pidSource ?? 'process'} pid ${processPid}`; const panePid = getFinitePid(runtime.panePid); if (panePid != null) return `pane pid ${panePid}`; return undefined; } function getFinitePid(value: number | undefined): number | undefined { return typeof value === 'number' && Number.isFinite(value) ? value : undefined; }