agent-ecosystem/src/renderer/utils/teamListStatus.ts
2026-04-28 23:15:32 +03:00

78 lines
2.2 KiB
TypeScript

import type { LeadActivityState, TeamProvisioningProgress, TeamSummary } from '@shared/types';
export type TeamStatus =
| 'active'
| 'idle'
| 'provisioning'
| 'offline'
| 'partial_failure'
| 'partial_skipped'
| 'partial_pending';
const ACTIVE_PROVISIONING_STATES = new Set<TeamProvisioningProgress['state']>([
'validating',
'spawning',
'configuring',
'assembling',
'finalizing',
'verifying',
]);
const READY_RUNNING_GRACE_MS = 45_000;
function isRecentReadyProgress(
currentProgress: TeamProvisioningProgress | null,
nowMs: number
): boolean {
if (currentProgress?.state !== 'ready') {
return false;
}
const updatedAtMs = Date.parse(currentProgress.updatedAt);
return Number.isFinite(updatedAtMs) && nowMs - updatedAtMs <= READY_RUNNING_GRACE_MS;
}
export function resolveTeamStatus(
team: TeamSummary,
teamName: string,
aliveTeams: string[],
currentProgress: TeamProvisioningProgress | null,
leadActivityByTeam: Partial<Record<string, LeadActivityState>>,
nowMs: number = Date.now()
): TeamStatus {
if (currentProgress && ACTIVE_PROVISIONING_STATES.has(currentProgress.state)) {
return 'provisioning';
}
const leadActivity = leadActivityByTeam[teamName];
if (leadActivity === 'offline') {
return 'offline';
}
if (aliveTeams.includes(teamName)) {
return leadActivity === 'active' ? 'active' : 'idle';
}
if (team.teamLaunchState === 'partial_pending') {
return 'partial_pending';
}
if (team.teamLaunchState === 'partial_skipped') {
return 'partial_skipped';
}
if (team.partialLaunchFailure || team.teamLaunchState === 'partial_failure') {
return 'partial_failure';
}
// The alive-list API is refreshed asynchronously after terminal launch progress.
// Keep a short optimistic running state to avoid a false Offline flicker between
// progress=ready and the next authoritative alive-list response.
if (isRecentReadyProgress(currentProgress, nowMs)) {
return leadActivity === 'active' ? 'active' : 'idle';
}
return 'offline';
}
export function isTeamListStatusRunning(status: TeamStatus): boolean {
return status !== 'offline' && status !== 'partial_failure' && status !== 'partial_pending';
}