diff --git a/src/main/services/team/TeamProvisioningService.ts b/src/main/services/team/TeamProvisioningService.ts index 56f60306..6d006174 100644 --- a/src/main/services/team/TeamProvisioningService.ts +++ b/src/main/services/team/TeamProvisioningService.ts @@ -3295,6 +3295,7 @@ export class TeamProvisioningService { private static readonly SAME_TEAM_RUN_START_SKEW_MS = 1_000; private static readonly SAME_TEAM_PERSIST_RETRY_MS = 2_000; private static readonly AGENT_RUNTIME_SNAPSHOT_CACHE_TTL_MS = 2_000; + private static readonly PERSISTED_AGENT_RUNTIME_SNAPSHOT_CACHE_TTL_MS = 5_000; private static readonly AGENT_RUNTIME_RESOURCE_HISTORY_LIMIT = 60; private static readonly BOOTSTRAP_TRANSCRIPT_OUTCOME_CACHE_MAX_ENTRIES = 2_048; private static readonly MAX_RUNTIME_TREE_PIDS_PER_ROOT = 64; @@ -4867,6 +4868,13 @@ export class TeamProvisioningService { return this.getProvisioningRunId(teamName) ?? this.getAliveRunId(teamName); } + private getAgentRuntimeSnapshotCacheTtlMs(teamName: string, runId: string | null): number { + if (runId || this.runtimeAdapterRunByTeam.has(teamName)) { + return TeamProvisioningService.AGENT_RUNTIME_SNAPSHOT_CACHE_TTL_MS; + } + return TeamProvisioningService.PERSISTED_AGENT_RUNTIME_SNAPSHOT_CACHE_TTL_MS; + } + private canDeliverToTrackedRuntimeRun(teamName: string, runId: string): boolean { const runtimeProgress = this.runtimeAdapterProgressByRunId.get(runId); if ( @@ -14589,7 +14597,7 @@ export class TeamProvisioningService { this.getTrackedRunId(teamName) === runId ) { this.agentRuntimeSnapshotCache.set(teamName, { - expiresAtMs: Date.now() + TeamProvisioningService.AGENT_RUNTIME_SNAPSHOT_CACHE_TTL_MS, + expiresAtMs: Date.now() + this.getAgentRuntimeSnapshotCacheTtlMs(teamName, runId), snapshot, }); } @@ -25330,7 +25338,7 @@ export class TeamProvisioningService { ); } this.runtimeProcessRowsForUsageSnapshotByTeam.set(teamName, { - expiresAtMs: Date.now() + TeamProvisioningService.AGENT_RUNTIME_SNAPSHOT_CACHE_TTL_MS, + expiresAtMs: Date.now() + this.getAgentRuntimeSnapshotCacheTtlMs(teamName, runId), generation: generationAtStart, runId, rows: processTableAvailable ? processRows : null, @@ -25491,7 +25499,7 @@ export class TeamProvisioningService { this.getTrackedRunId(teamName) === runId ) { this.liveTeamAgentRuntimeMetadataCache.set(teamName, { - expiresAtMs: Date.now() + TeamProvisioningService.AGENT_RUNTIME_SNAPSHOT_CACHE_TTL_MS, + expiresAtMs: Date.now() + this.getAgentRuntimeSnapshotCacheTtlMs(teamName, runId), metadata: this.cloneLiveTeamAgentRuntimeMetadata(metadataByMember), runId, }); @@ -25796,7 +25804,9 @@ export class TeamProvisioningService { const resultRows = rows && rows.length > 0 ? rows : runtimeProcessTableAvailable ? [] : null; this.runtimeProcessRowsForUsageSnapshotByTeam.set(teamName, { - expiresAtMs: Date.now() + TeamProvisioningService.AGENT_RUNTIME_SNAPSHOT_CACHE_TTL_MS, + expiresAtMs: + Date.now() + + this.getAgentRuntimeSnapshotCacheTtlMs(teamName, this.getTrackedRunId(teamName)), generation: this.getRuntimeSnapshotCacheGeneration(teamName), runId: this.getTrackedRunId(teamName), rows: resultRows, diff --git a/test/main/services/team/TeamProvisioningService.test.ts b/test/main/services/team/TeamProvisioningService.test.ts index af2565a0..b049b846 100644 --- a/test/main/services/team/TeamProvisioningService.test.ts +++ b/test/main/services/team/TeamProvisioningService.test.ts @@ -3385,6 +3385,51 @@ describe('TeamProvisioningService', () => { expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(1); }); + it('uses a longer live runtime metadata cache for persisted teams without a tracked run', async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date('2026-05-03T12:00:00.000Z')); + const svc = new TeamProvisioningService(); + (svc as any).configReader = { + getConfig: vi.fn(async () => ({ + members: [ + { name: 'team-lead', agentType: 'team-lead' }, + { name: 'alice', model: 'gpt-5.4-mini' }, + ], + })), + }; + vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([]); + + await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team'); + vi.setSystemTime(new Date('2026-05-03T12:00:03.000Z')); + await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team'); + vi.setSystemTime(new Date('2026-05-03T12:00:06.000Z')); + await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team'); + + expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(2); + }); + + it('keeps the short live runtime metadata cache for tracked runs', async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date('2026-05-03T12:00:00.000Z')); + const svc = new TeamProvisioningService(); + (svc as any).configReader = { + getConfig: vi.fn(async () => ({ + members: [ + { name: 'team-lead', agentType: 'team-lead' }, + { name: 'alice', model: 'gpt-5.4-mini' }, + ], + })), + }; + (svc as any).aliveRunByTeam.set('runtime-team', 'run-1'); + vi.mocked(listRuntimeProcessTableForCurrentPlatform).mockResolvedValue([]); + + await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team'); + vi.setSystemTime(new Date('2026-05-03T12:00:03.000Z')); + await (svc as any).getLiveTeamAgentRuntimeMetadata('runtime-team'); + + expect(listRuntimeProcessTableForCurrentPlatform).toHaveBeenCalledTimes(2); + }); + it('clears runtime probe caches when starting a new run for the team', async () => { const svc = new TeamProvisioningService(); (svc as any).configReader = {