fix(team): keep codex member work visible during recovery

This commit is contained in:
777genius 2026-05-18 23:31:58 +03:00
parent e5ace8c7cb
commit f19ed93d4b
10 changed files with 535 additions and 61 deletions

View file

@ -1,27 +1,27 @@
{
"version": "0.0.37",
"sourceRef": "v0.0.37",
"version": "0.0.38",
"sourceRef": "v0.0.38",
"sourceRepository": "777genius/agent_teams_orchestrator",
"releaseRepository": "777genius/agent-teams-ai",
"releaseTag": "v2.0.0",
"assets": {
"darwin-arm64": {
"file": "agent-teams-runtime-darwin-arm64-v0.0.37.tar.gz",
"file": "agent-teams-runtime-darwin-arm64-v0.0.38.tar.gz",
"archiveKind": "tar.gz",
"binaryName": "claude-multimodel"
},
"darwin-x64": {
"file": "agent-teams-runtime-darwin-x64-v0.0.37.tar.gz",
"file": "agent-teams-runtime-darwin-x64-v0.0.38.tar.gz",
"archiveKind": "tar.gz",
"binaryName": "claude-multimodel"
},
"linux-x64": {
"file": "agent-teams-runtime-linux-x64-v0.0.37.tar.gz",
"file": "agent-teams-runtime-linux-x64-v0.0.38.tar.gz",
"archiveKind": "tar.gz",
"binaryName": "claude-multimodel"
},
"win32-x64": {
"file": "agent-teams-runtime-win32-x64-v0.0.37.zip",
"file": "agent-teams-runtime-win32-x64-v0.0.38.zip",
"archiveKind": "zip",
"binaryName": "claude-multimodel.exe"
}

View file

@ -0,0 +1,106 @@
import { describe, expect, it } from 'vitest';
import {
hasWorkSyncActiveRuntime,
isRuntimeEntryActiveForWorkSync,
} from '../memberWorkSyncTeamActivity';
import type { TeamAgentRuntimeEntry, TeamAgentRuntimeSnapshot } from '@shared/types';
function createRuntimeEntry(overrides: Partial<TeamAgentRuntimeEntry> = {}): TeamAgentRuntimeEntry {
return {
memberName: 'alice',
alive: true,
restartable: true,
backendType: 'process',
providerId: 'codex',
providerBackendId: 'codex-native',
livenessKind: 'runtime_process',
pid: 46773,
updatedAt: '2026-05-18T19:44:48.000Z',
...overrides,
};
}
function createRuntimeSnapshot(
members: Record<string, TeamAgentRuntimeEntry>
): TeamAgentRuntimeSnapshot {
return {
teamName: 'signal-ops-6',
updatedAt: '2026-05-18T19:44:48.000Z',
runId: null,
members,
};
}
describe('member work sync team activity', () => {
it('treats a verified runtime process as active', () => {
expect(isRuntimeEntryActiveForWorkSync(createRuntimeEntry())).toBe(true);
});
it('treats a confirmed bootstrap runtime entry as active', () => {
expect(
isRuntimeEntryActiveForWorkSync(
createRuntimeEntry({
livenessKind: 'confirmed_bootstrap',
runtimeLastSeenAt: '2026-05-18T19:44:47.000Z',
})
)
).toBe(true);
});
it('does not treat inactive liveness diagnostics as active by themselves', () => {
for (const livenessKind of [
'permission_blocked',
'runtime_process_candidate',
'shell_only',
'registered_only',
'stale_metadata',
'not_found',
] as const) {
expect(isRuntimeEntryActiveForWorkSync(createRuntimeEntry({ livenessKind }))).toBe(false);
}
});
it('does not treat a runtime candidate as active until it is alive', () => {
expect(
isRuntimeEntryActiveForWorkSync(
createRuntimeEntry({
alive: false,
livenessKind: 'runtime_process_candidate',
})
)
).toBe(false);
});
it('detects an active runtime among stale members', () => {
expect(
hasWorkSyncActiveRuntime(
createRuntimeSnapshot({
alice: createRuntimeEntry({ alive: false, livenessKind: 'stale_metadata' }),
bob: createRuntimeEntry({ memberName: 'bob', livenessKind: 'runtime_process' }),
})
)
).toBe(true);
});
it('returns false when no member has active runtime evidence', () => {
expect(
hasWorkSyncActiveRuntime(
createRuntimeSnapshot({
alice: createRuntimeEntry({ alive: false, livenessKind: 'stale_metadata' }),
bob: createRuntimeEntry({
memberName: 'bob',
alive: false,
livenessKind: 'registered_only',
}),
})
)
).toBe(false);
});
it('handles missing snapshots as inactive', () => {
expect(hasWorkSyncActiveRuntime(null)).toBe(false);
expect(hasWorkSyncActiveRuntime(undefined)).toBe(false);
});
});

View file

@ -0,0 +1,30 @@
import type { TeamAgentRuntimeEntry, TeamAgentRuntimeSnapshot } from '@shared/types';
type RuntimeLivenessKind = NonNullable<TeamAgentRuntimeEntry['livenessKind']>;
const WORK_SYNC_INACTIVE_LIVENESS_KINDS = new Set<RuntimeLivenessKind>([
'permission_blocked',
'runtime_process_candidate',
'shell_only',
'registered_only',
'stale_metadata',
'not_found',
]);
export function isRuntimeEntryActiveForWorkSync(
entry: Pick<TeamAgentRuntimeEntry, 'alive' | 'livenessKind'> | null | undefined
): boolean {
if (entry?.alive !== true) {
return false;
}
if (!entry.livenessKind) {
return true;
}
return !WORK_SYNC_INACTIVE_LIVENESS_KINDS.has(entry.livenessKind);
}
export function hasWorkSyncActiveRuntime(
snapshot: Pick<TeamAgentRuntimeSnapshot, 'members'> | null | undefined
): boolean {
return Object.values(snapshot?.members ?? {}).some(isRuntimeEntryActiveForWorkSync);
}

View file

@ -8,3 +8,7 @@ export {
buildMemberWorkSyncRuntimeTurnSettledEnvironment,
createMemberWorkSyncFeature,
} from './composition/createMemberWorkSyncFeature';
export {
hasWorkSyncActiveRuntime,
isRuntimeEntryActiveForWorkSync,
} from './composition/memberWorkSyncTeamActivity';

View file

@ -40,6 +40,7 @@ import {
import {
buildMemberWorkSyncRuntimeTurnSettledEnvironment,
createMemberWorkSyncFeature,
hasWorkSyncActiveRuntime,
type MemberWorkSyncFeatureFacade,
registerMemberWorkSyncIpc,
removeMemberWorkSyncIpc,
@ -1780,27 +1781,55 @@ async function initializeServices(): Promise<void> {
logger: createLogger('Feature:RecentProjects'),
});
runtimeProviderManagementFeature = createRuntimeProviderManagementFeature();
const memberWorkSyncLogger = createLogger('Feature:MemberWorkSync');
const hasMemberWorkSyncRuntimeActivity = async (teamName: string): Promise<boolean> => {
try {
const snapshot = await teamProvisioningService.getTeamAgentRuntimeSnapshot(teamName);
return hasWorkSyncActiveRuntime(snapshot);
} catch (error) {
memberWorkSyncLogger.warn('member work sync runtime activity check failed', {
teamName,
error: String(error),
});
return false;
}
};
const isTeamActiveForMemberWorkSync = async (teamName: string): Promise<boolean> => {
if (
teamProvisioningService.isTeamAlive(teamName) ||
teamProvisioningService.hasProvisioningRun(teamName)
) {
return true;
}
return hasMemberWorkSyncRuntimeActivity(teamName);
};
const canDispatchMemberWorkSyncNudges = async (teamName: string): Promise<boolean> => {
if (teamProvisioningService.isTeamAlive(teamName)) {
return true;
}
return hasMemberWorkSyncRuntimeActivity(teamName);
};
const listMemberWorkSyncLifecycleActiveTeamNames = async (): Promise<string[]> => {
const activeTeamNames: string[] = [];
for (const team of await teamDataService.listTeams()) {
if (team.deletedAt) {
continue;
}
if (await isTeamActiveForMemberWorkSync(team.teamName)) {
activeTeamNames.push(team.teamName);
}
}
return activeTeamNames;
};
memberWorkSyncFeature = createMemberWorkSyncFeature({
teamsBasePath: getTeamsBasePath(),
configReader: new TeamConfigReader(),
taskReader: new TeamTaskReader(),
kanbanManager: new TeamKanbanManager(),
membersMetaStore: new TeamMembersMetaStore(),
isTeamActive: (teamName) =>
teamProvisioningService.isTeamAlive(teamName) ||
teamProvisioningService.hasProvisioningRun(teamName),
canDispatchNudges: (teamName) => teamProvisioningService.isTeamAlive(teamName),
listLifecycleActiveTeamNames: async () => {
const teams = await teamDataService.listTeams();
return teams
.filter(
(team) =>
!team.deletedAt &&
(teamProvisioningService.isTeamAlive(team.teamName) ||
teamProvisioningService.hasProvisioningRun(team.teamName))
)
.map((team) => team.teamName);
},
isTeamActive: isTeamActiveForMemberWorkSync,
canDispatchNudges: canDispatchMemberWorkSyncNudges,
listLifecycleActiveTeamNames: listMemberWorkSyncLifecycleActiveTeamNames,
extraBusySignals: [
{
isBusy: (input) => teamProvisioningService.getOpenCodeMemberDeliveryBusyStatus(input),
@ -1984,7 +2013,7 @@ async function initializeServices(): Promise<void> {
});
},
},
logger: createLogger('Feature:MemberWorkSync'),
logger: memberWorkSyncLogger,
});
teamProvisioningService.setRuntimeTurnSettledHookSettingsProvider((input) =>
memberWorkSyncFeature

View file

@ -16,6 +16,7 @@ import {
buildMemberLaunchPresentation,
displayMemberName,
isOpenCodeRelaunchActionable,
shouldDisplayMemberCurrentTask,
} from '@renderer/utils/memberHelpers';
import {
buildMemberLaunchDiagnosticsPayload,
@ -650,8 +651,18 @@ export const MemberCard = memo(function MemberCard({
selectedTeamName ? selectResolvedMembersForTeamName(s, selectedTeamName) : []
);
const avatarMap = useMemo(() => buildMemberAvatarMap(teamMembers), [teamMembers]);
const showTaskActivity = shouldDisplayMemberCurrentTask({
member,
isTeamAlive,
spawnStatus,
spawnLaunchState,
spawnRuntimeAlive,
runtimeEntry,
});
const visibleCurrentTask = showTaskActivity ? currentTask : null;
const visibleReviewTask = showTaskActivity ? reviewTask : null;
const presentationMember =
member.currentTaskId && !currentTask
member.currentTaskId && !visibleCurrentTask
? {
...member,
currentTaskId: null,
@ -716,11 +727,11 @@ export const MemberCard = memo(function MemberCard({
workspacePath ? `Path: ${workspacePath}` : 'Path is not available yet.',
member.gitBranch ? `Branch: ${member.gitBranch}` : null,
].filter((line): line is string => Boolean(line));
const activityTask = currentTask ?? reviewTask ?? null;
const activityTitle = currentTask
? `Current task: #${deriveTaskDisplayId(currentTask.id)}`
: reviewTask
? `Reviewing task: #${deriveTaskDisplayId(reviewTask.id)}`
const activityTask = visibleCurrentTask ?? visibleReviewTask ?? null;
const activityTitle = visibleCurrentTask
? `Current task: #${deriveTaskDisplayId(visibleCurrentTask.id)}`
: visibleReviewTask
? `Reviewing task: #${deriveTaskDisplayId(visibleReviewTask.id)}`
: undefined;
const runtimeTelemetryTitle = buildRuntimeTelemetryTitle(runtimeEntry);
const showRuntimeTelemetryTooltip = Boolean(runtimeTelemetryTitle);
@ -1060,9 +1071,9 @@ export const MemberCard = memo(function MemberCard({
</TooltipContent>
</Tooltip>
) : null}
{currentTask ? (
{visibleCurrentTask ? (
<CurrentTaskIndicator
task={currentTask}
task={visibleCurrentTask}
borderColor={colors.border}
activityLabel="working on"
activityTimer={currentTaskTimer}
@ -1070,9 +1081,9 @@ export const MemberCard = memo(function MemberCard({
onOpenTask={onOpenTask}
/>
) : null}
{reviewTask ? (
{visibleReviewTask ? (
<CurrentTaskIndicator
task={reviewTask}
task={visibleReviewTask}
borderColor={colors.border}
activityLabel={reviewTaskTimer ? 'reviewing' : 'review requested'}
activityTimer={reviewTaskTimer}

View file

@ -203,7 +203,7 @@ export const MemberHoverCard = memo(function MemberHoverCard({
const showCopyDiagnostics =
hasMemberLaunchDiagnosticsError(launchDiagnosticsPayload) &&
hasMemberLaunchDiagnosticsDetails(launchDiagnosticsPayload);
const reviewTask: TeamTaskWithKanban | null = tasks
const reviewTaskCandidate: TeamTaskWithKanban | null = tasks
? (tasks.find(
(task) =>
task.reviewer === member.name &&
@ -211,6 +211,18 @@ export const MemberHoverCard = memo(function MemberHoverCard({
getTeamTaskWorkflowColumn(task) === 'review'
) ?? null)
: null;
const reviewTask =
reviewTaskCandidate &&
shouldDisplayMemberCurrentTask({
member,
isTeamAlive,
spawnStatus: spawnEntry?.status,
spawnLaunchState: spawnEntry?.launchState,
spawnRuntimeAlive: spawnEntry?.runtimeAlive,
runtimeEntry,
})
? reviewTaskCandidate
: null;
return (
<HoverCard openDelay={300} closeDelay={200}>

View file

@ -769,32 +769,18 @@ export const MemberList = memo(function MemberList({
const isMemberActivityTimerRunning = useCallback(
(
member: ResolvedTeamMember,
spawnEntry: MemberSpawnStatusEntry | undefined,
runtimeEntry: TeamAgentRuntimeEntry | undefined
): boolean => {
if (isTeamAlive === false) return false;
if (
spawnEntry?.status === 'offline' ||
spawnEntry?.status === 'error' ||
spawnEntry?.status === 'skipped'
) {
return false;
}
if (spawnEntry?.runtimeAlive === false) {
return false;
}
if (runtimeEntry?.alive === false) {
return false;
}
if (
runtimeEntry?.livenessKind === 'shell_only' ||
runtimeEntry?.livenessKind === 'registered_only' ||
runtimeEntry?.livenessKind === 'stale_metadata' ||
runtimeEntry?.livenessKind === 'not_found'
) {
return false;
}
return true;
return shouldDisplayMemberCurrentTask({
member,
isTeamAlive,
spawnStatus: spawnEntry?.status,
spawnLaunchState: spawnEntry?.launchState,
spawnRuntimeAlive: spawnEntry?.runtimeAlive,
runtimeEntry,
});
},
[isTeamAlive]
);
@ -827,7 +813,7 @@ export const MemberList = memo(function MemberList({
for (const member of activeMembers) {
const spawnEntry = memberSpawnStatuses?.get(member.name);
const runtimeEntry = memberRuntimeEntries?.get(member.name);
const running = isMemberActivityTimerRunning(spawnEntry, runtimeEntry);
const running = isMemberActivityTimerRunning(member, spawnEntry, runtimeEntry);
const currentTaskCandidate = member.currentTaskId
? (taskMap.get(member.currentTaskId) ?? null)
: null;
@ -948,8 +934,23 @@ export const MemberList = memo(function MemberList({
: null;
const reviewCandidate = reviewTaskByMember.get(member.name) ?? null;
const reviewTask =
reviewCandidate && reviewCandidate.id !== currentTask?.id ? reviewCandidate : null;
const activityTimerRunning = isMemberActivityTimerRunning(spawnEntry, runtimeEntry);
reviewCandidate &&
reviewCandidate.id !== currentTask?.id &&
shouldDisplayMemberCurrentTask({
member,
isTeamAlive,
spawnStatus: spawnEntry?.status,
spawnLaunchState: spawnEntry?.launchState,
spawnRuntimeAlive: spawnEntry?.runtimeAlive,
runtimeEntry,
})
? reviewCandidate
: null;
const activityTimerRunning = isMemberActivityTimerRunning(
member,
spawnEntry,
runtimeEntry
);
const currentTaskTimer = withActivityTimerRunId(
currentTask
? deriveWorkActivityTimerAnchor(currentTask, {

View file

@ -0,0 +1,172 @@
import { describe, expect, it } from 'vitest';
import { buildMemberLaunchPresentation, shouldDisplayMemberCurrentTask } from '../memberHelpers';
import type {
MemberLaunchState,
MemberSpawnStatus,
ResolvedTeamMember,
TeamAgentRuntimeEntry,
} from '@shared/types';
function createMember(overrides: Partial<ResolvedTeamMember> = {}): ResolvedTeamMember {
return {
name: 'alice',
status: 'active',
currentTaskId: 'task-1',
taskCount: 1,
lastActiveAt: null,
messageCount: 0,
providerId: 'codex',
providerBackendId: 'codex-native',
role: 'developer',
...overrides,
};
}
function createLiveRuntime(overrides: Partial<TeamAgentRuntimeEntry> = {}): TeamAgentRuntimeEntry {
return {
memberName: 'alice',
alive: true,
restartable: true,
backendType: 'process',
providerId: 'codex',
providerBackendId: 'codex-native',
livenessKind: 'runtime_process',
pid: 12345,
rssBytes: 128 * 1024 * 1024,
updatedAt: '2026-05-18T19:45:00.000Z',
...overrides,
};
}
function createConfirmedCodexSpawn(): {
spawnStatus: MemberSpawnStatus;
spawnLaunchState: MemberLaunchState;
spawnRuntimeAlive: boolean;
spawnBootstrapConfirmed: boolean;
} {
return {
spawnStatus: 'online',
spawnLaunchState: 'confirmed_alive',
spawnRuntimeAlive: true,
spawnBootstrapConfirmed: true,
};
}
describe('member runtime presentation', () => {
it('hides Codex native task activity when no spawn or runtime snapshot has loaded', () => {
expect(
shouldDisplayMemberCurrentTask({
member: createMember(),
isTeamAlive: true,
})
).toBe(false);
});
it('hides Codex native task activity when confirmed spawn state has no live runtime evidence', () => {
expect(
shouldDisplayMemberCurrentTask({
member: createMember(),
isTeamAlive: true,
...createConfirmedCodexSpawn(),
})
).toBe(false);
});
it('keeps Codex native task activity visible when the runtime process is live', () => {
expect(
shouldDisplayMemberCurrentTask({
member: createMember(),
isTeamAlive: true,
...createConfirmedCodexSpawn(),
runtimeEntry: createLiveRuntime(),
})
).toBe(true);
});
it('hides Codex native task activity for runtime process candidates without verified process evidence', () => {
expect(
shouldDisplayMemberCurrentTask({
member: createMember(),
isTeamAlive: true,
...createConfirmedCodexSpawn(),
runtimeEntry: createLiveRuntime({
livenessKind: 'runtime_process_candidate',
rssBytes: undefined,
}),
})
).toBe(false);
});
it('marks stale confirmed Codex native spawn state as non-green runtime status', () => {
const presentation = buildMemberLaunchPresentation({
member: createMember(),
spawnLivenessSource: 'heartbeat',
runtimeAdvisory: undefined,
isTeamAlive: true,
isTeamProvisioning: false,
...createConfirmedCodexSpawn(),
});
expect(presentation.launchVisualState).toBe('stale_runtime');
expect(presentation.presenceLabel).toBe('stale runtime');
expect(presentation.dotClass).toContain('bg-red-400');
});
it('marks Codex native members without runtime snapshots as stale after launch settles', () => {
const presentation = buildMemberLaunchPresentation({
member: createMember(),
spawnStatus: undefined,
spawnLaunchState: undefined,
spawnRuntimeAlive: undefined,
spawnBootstrapConfirmed: undefined,
spawnLivenessSource: undefined,
runtimeAdvisory: undefined,
isTeamAlive: true,
isTeamProvisioning: false,
});
expect(presentation.launchVisualState).toBe('stale_runtime');
expect(presentation.dotClass).toContain('bg-red-400');
});
it('hides Codex native activity until runtime evidence arrives', () => {
expect(
shouldDisplayMemberCurrentTask({
member: createMember(),
isTeamAlive: true,
})
).toBe(false);
});
it('does not let a global launch settling state keep stale Codex native status green', () => {
const presentation = buildMemberLaunchPresentation({
member: createMember(),
spawnLivenessSource: 'heartbeat',
runtimeAdvisory: undefined,
isTeamAlive: true,
isTeamProvisioning: false,
isLaunchSettling: true,
...createConfirmedCodexSpawn(),
});
expect(presentation.launchVisualState).toBe('stale_runtime');
expect(presentation.dotClass).toContain('bg-red-400');
});
it('does not require runtime evidence for non-Codex teammates', () => {
expect(
shouldDisplayMemberCurrentTask({
member: createMember({
providerId: 'anthropic',
providerBackendId: undefined,
}),
isTeamAlive: true,
spawnStatus: 'online',
spawnLaunchState: 'confirmed_alive',
spawnRuntimeAlive: true,
})
).toBe(true);
});
});

View file

@ -935,10 +935,13 @@ function getLaunchVisualStateDotClass(visualState: MemberLaunchVisualState): str
}
function getCurrentRuntimeOfflineVisualState(
member: ResolvedTeamMember,
runtimeEntry: TeamAgentRuntimeEntry | undefined,
spawnStatus: MemberSpawnStatus | undefined,
spawnLaunchState: MemberLaunchState | undefined,
spawnRuntimeAlive: boolean | undefined
spawnRuntimeAlive: boolean | undefined,
spawnBootstrapConfirmed: boolean | undefined,
isTeamProvisioning: boolean | undefined
): MemberLaunchVisualState {
if (runtimeEntry?.livenessKind === 'registered_only') {
return 'registered_only';
@ -963,9 +966,109 @@ function getCurrentRuntimeOfflineVisualState(
) {
return 'stale_runtime';
}
if (
shouldTreatCodexNativeRuntimeAsOffline({
member,
runtimeEntry,
spawnStatus,
spawnLaunchState,
spawnRuntimeAlive,
spawnBootstrapConfirmed,
isTeamProvisioning,
})
) {
return 'stale_runtime';
}
return null;
}
function isCodexNativeProcessTeammate(member: ResolvedTeamMember): boolean {
if (isLeadMember(member)) {
return false;
}
return (
member.providerId === 'codex' &&
(member.providerBackendId == null || member.providerBackendId === 'codex-native')
);
}
function hasLiveRuntimeProcessEvidence(runtimeEntry: TeamAgentRuntimeEntry | undefined): boolean {
if (runtimeEntry?.alive !== true) {
return false;
}
return (
runtimeEntry.livenessKind == null ||
runtimeEntry.livenessKind === 'runtime_process' ||
runtimeEntry.livenessKind === 'confirmed_bootstrap'
);
}
function hasSpawnRuntimeLiveClaim({
spawnStatus,
spawnLaunchState,
spawnRuntimeAlive,
spawnBootstrapConfirmed,
}: {
spawnStatus?: MemberSpawnStatus;
spawnLaunchState?: MemberLaunchState;
spawnRuntimeAlive?: boolean;
spawnBootstrapConfirmed?: boolean;
}): boolean {
return (
spawnStatus === 'online' ||
spawnLaunchState === 'confirmed_alive' ||
spawnRuntimeAlive === true ||
spawnBootstrapConfirmed === true
);
}
function shouldTreatCodexNativeRuntimeAsOffline({
member,
runtimeEntry,
spawnStatus,
spawnLaunchState,
spawnRuntimeAlive,
spawnBootstrapConfirmed,
isTeamProvisioning,
}: {
member: ResolvedTeamMember;
runtimeEntry?: TeamAgentRuntimeEntry;
spawnStatus?: MemberSpawnStatus;
spawnLaunchState?: MemberLaunchState;
spawnRuntimeAlive?: boolean;
spawnBootstrapConfirmed?: boolean;
isTeamProvisioning?: boolean;
}): boolean {
if (!isCodexNativeProcessTeammate(member)) {
return false;
}
if (hasLiveRuntimeProcessEvidence(runtimeEntry)) {
return false;
}
if (
isTeamProvisioning === true &&
runtimeEntry == null &&
!hasSpawnRuntimeLiveClaim({
spawnStatus,
spawnLaunchState,
spawnRuntimeAlive,
spawnBootstrapConfirmed,
})
) {
return false;
}
return (
runtimeEntry != null ||
hasSpawnRuntimeLiveClaim({
spawnStatus,
spawnLaunchState,
spawnRuntimeAlive,
spawnBootstrapConfirmed,
}) ||
spawnStatus == null
);
}
export function shouldDisplayMemberCurrentTask({
member,
isTeamAlive,
@ -1011,6 +1114,9 @@ export function shouldDisplayMemberCurrentTask({
if (spawnRuntimeAlive === false) {
return false;
}
if (isCodexNativeProcessTeammate(member) && !hasLiveRuntimeProcessEvidence(runtimeEntry)) {
return false;
}
return true;
}
@ -1205,10 +1311,13 @@ export function buildMemberLaunchPresentation({
nowMs?: number;
}): MemberLaunchPresentation {
const currentRuntimeOfflineVisualState = getCurrentRuntimeOfflineVisualState(
member,
runtimeEntry,
spawnStatus,
spawnLaunchState,
spawnRuntimeAlive
spawnRuntimeAlive,
spawnBootstrapConfirmed,
isTeamProvisioning
);
const hasConfirmedSpawnLaunch =
spawnLaunchState === 'confirmed_alive' && spawnBootstrapConfirmed === true;