agent-ecosystem/src/renderer/utils/memberRuntimeSummary.ts

202 lines
7.6 KiB
TypeScript

import { formatTeamModelSummary } from '@renderer/components/team/dialogs/TeamModelSelector';
import { formatBytes } from '@renderer/utils/formatters';
import { formatTeamProviderBackendLabel } from '@renderer/utils/providerBackendIdentity';
import { extractProviderScopedBaseModel } from '@renderer/utils/teamModelContext';
import { isLeadMember } from '@shared/utils/leadDetection';
import { inferTeamProviderIdFromModel } from '@shared/utils/teamProvider';
import type { TeamLaunchParams } from '@renderer/store/slices/teamSlice';
import type {
MemberSpawnStatusEntry,
ResolvedTeamMember,
TeamAgentRuntimeEntry,
TeamProviderId,
} from '@shared/types';
function shouldShowRuntimeMemory(
spawnEntry: MemberSpawnStatusEntry | undefined,
runtimeEntry: TeamAgentRuntimeEntry | undefined
): boolean {
if (typeof runtimeEntry?.rssBytes !== 'number' || runtimeEntry.rssBytes <= 0) {
return false;
}
if (
spawnEntry?.status === 'offline' ||
spawnEntry?.status === 'skipped' ||
spawnEntry?.launchState === 'skipped_for_launch'
) {
return false;
}
if (!spawnEntry) {
return runtimeEntry.alive === true;
}
return (
runtimeEntry.alive === true ||
spawnEntry.runtimeAlive === true ||
spawnEntry.bootstrapConfirmed === true ||
spawnEntry.livenessSource === 'process' ||
spawnEntry.livenessSource === 'heartbeat'
);
}
function normalizeMemberBackendLabel(
providerId: TeamProviderId,
backendLabel: string | undefined
): string | undefined {
if (!backendLabel) {
return undefined;
}
if (providerId === 'codex' && backendLabel === 'Codex native') {
return 'Codex';
}
return backendLabel;
}
function isMemberLaunchPending(spawnEntry: MemberSpawnStatusEntry | undefined): boolean {
if (!spawnEntry) {
return false;
}
return (
spawnEntry.launchState === 'starting' ||
spawnEntry.launchState === 'runtime_pending_bootstrap' ||
spawnEntry.launchState === 'runtime_pending_permission' ||
spawnEntry.status === 'waiting' ||
spawnEntry.status === 'spawning'
);
}
function appendRuntimeSummarySuffixes(
summary: string,
backendLabel: string | undefined,
memorySuffix: string
): string {
const summaryParts = new Set(summary.split(' · '));
const backendSuffix = backendLabel && !summaryParts.has(backendLabel) ? ` · ${backendLabel}` : '';
return `${summary}${backendSuffix}${memorySuffix}`;
}
export function getRuntimeMemorySourceLabel(
runtimeEntry: TeamAgentRuntimeEntry | undefined
): string | undefined {
if (!runtimeEntry?.pidSource) {
return undefined;
}
if (runtimeEntry.pidSource === 'tmux_pane') {
return 'RSS source: tmux pane shell';
}
if (
runtimeEntry.providerId === 'opencode' &&
runtimeEntry.restartable === false &&
runtimeEntry.pidSource === 'opencode_bridge'
) {
return 'RSS source: shared OpenCode host';
}
if (runtimeEntry.pidSource === 'tmux_child' || runtimeEntry.pidSource === 'agent_process_table') {
return 'RSS source: runtime process';
}
if (runtimeEntry.pidSource === 'lead_process') {
return 'RSS source: lead process';
}
if (runtimeEntry.pidSource === 'runtime_bootstrap') {
return 'RSS source: runtime bootstrap process';
}
if (runtimeEntry.pidSource === 'persisted_metadata') {
return 'RSS source: persisted runtime metadata';
}
return `PID source: ${runtimeEntry.pidSource}`;
}
export function resolveMemberRuntimeSummary(
member: ResolvedTeamMember,
launchParams: TeamLaunchParams | undefined,
spawnEntry: MemberSpawnStatusEntry | undefined,
runtimeEntry?: TeamAgentRuntimeEntry
): string | undefined {
const leadLaunchParams = isLeadMember(member) ? launchParams : undefined;
const memberProviderBackendId = (member as ResolvedTeamMember & { providerBackendId?: string })
.providerBackendId;
const memberModel = member.model?.trim() || '';
const runtimeModel = spawnEntry?.runtimeModel?.trim() || runtimeEntry?.runtimeModel?.trim();
const runtimeModelProvider = inferTeamProviderIdFromModel(runtimeModel);
const inferredMemberProvider = inferTeamProviderIdFromModel(memberModel) ?? runtimeModelProvider;
const launchPending = isMemberLaunchPending(spawnEntry);
const stalePrimaryLaneConflictsWithLaunch =
!leadLaunchParams &&
launchPending &&
launchParams?.providerId != null &&
member.laneKind === 'primary' &&
member.laneOwnerProviderId != null &&
member.laneOwnerProviderId !== launchParams.providerId;
const authoritativeLaunchParams =
leadLaunchParams ?? (stalePrimaryLaneConflictsWithLaunch ? launchParams : undefined);
const configuredProvider: TeamProviderId =
authoritativeLaunchParams?.providerId ??
member.providerId ??
inferredMemberProvider ??
launchParams?.providerId ??
'anthropic';
const memberProviderForInheritance =
authoritativeLaunchParams?.providerId ?? member.providerId ?? inferredMemberProvider;
const inheritsLeadRuntimeDefaults =
memberProviderForInheritance == null ||
launchParams?.providerId == null ||
memberProviderForInheritance === launchParams.providerId;
const configuredModel = authoritativeLaunchParams
? authoritativeLaunchParams.model?.trim() || ''
: memberModel || (inheritsLeadRuntimeDefaults ? launchParams?.model?.trim() || '' : '');
const configuredEffort = authoritativeLaunchParams
? authoritativeLaunchParams.effort
: (member.effort ?? (inheritsLeadRuntimeDefaults ? launchParams?.effort : undefined));
const configuredProviderBackendId = authoritativeLaunchParams
? authoritativeLaunchParams.providerBackendId
: (memberProviderBackendId ??
(inheritsLeadRuntimeDefaults ? launchParams?.providerBackendId : undefined));
const runtimeProviderId = runtimeModelProvider ?? runtimeEntry?.providerId;
const runtimeModelConflictsWithAuthoritativeLaunch =
launchPending &&
authoritativeLaunchParams != null &&
runtimeModel != null &&
(authoritativeLaunchParams.model == null ||
extractProviderScopedBaseModel(runtimeModel, runtimeProviderId ?? configuredProvider) !==
extractProviderScopedBaseModel(
authoritativeLaunchParams.model,
authoritativeLaunchParams.providerId
));
const runtimeConflictsWithAuthoritativeLaunch =
authoritativeLaunchParams?.providerId != null &&
(stalePrimaryLaneConflictsWithLaunch ||
(runtimeProviderId != null && runtimeProviderId !== authoritativeLaunchParams.providerId) ||
runtimeModelConflictsWithAuthoritativeLaunch);
const displayRuntimeModel = runtimeConflictsWithAuthoritativeLaunch ? undefined : runtimeModel;
const backendLabel = normalizeMemberBackendLabel(
configuredProvider,
formatTeamProviderBackendLabel(configuredProvider, configuredProviderBackendId)
);
const memorySuffix =
!runtimeConflictsWithAuthoritativeLaunch && shouldShowRuntimeMemory(spawnEntry, runtimeEntry)
? ` · ${formatBytes(runtimeEntry!.rssBytes!)}`
: '';
if (displayRuntimeModel && (launchPending || configuredModel.length === 0)) {
const runtimeProvider = runtimeModelProvider ?? configuredProvider;
const summary = formatTeamModelSummary(runtimeProvider, displayRuntimeModel, configuredEffort);
return appendRuntimeSummarySuffixes(summary, backendLabel, memorySuffix);
}
if (launchPending) {
if (!authoritativeLaunchParams && !configuredModel.length && !memorySuffix) {
return undefined;
}
const summary = formatTeamModelSummary(configuredProvider, configuredModel, configuredEffort);
return appendRuntimeSummarySuffixes(summary, backendLabel, memorySuffix);
}
const summary = formatTeamModelSummary(configuredProvider, configuredModel, configuredEffort);
return appendRuntimeSummarySuffixes(summary, backendLabel, memorySuffix);
}