diff --git a/src/commands/status.summary.test.ts b/src/commands/status.summary.test.ts index 2d6b539bbdd..ba21ee4e5bf 100644 --- a/src/commands/status.summary.test.ts +++ b/src/commands/status.summary.test.ts @@ -510,6 +510,40 @@ describe("getStatusSummary", () => { expect(hydratedKeys).not.toContain("agent:main:session-2"); }); + it("preserves store order for tied recent session timestamps", async () => { + const store = Object.fromEntries( + Array.from({ length: 11 }, (_, index) => { + const number = index + 1; + return [ + `agent:main:session-${number}`, + { + sessionId: `session-${number}`, + updatedAt: 1, + }, + ]; + }), + ); + statusSummaryMocks.listSessionEntries.mockReturnValue(toSessionEntrySummaries(store)); + + const summary = await getStatusSummary(); + + expect(summary.sessions.recent.map((session) => session.key)).toEqual([ + "agent:main:session-1", + "agent:main:session-2", + "agent:main:session-3", + "agent:main:session-4", + "agent:main:session-5", + "agent:main:session-6", + "agent:main:session-7", + "agent:main:session-8", + "agent:main:session-9", + "agent:main:session-10", + ]); + expect(summary.sessions.byAgent[0]?.recent.map((session) => session.key)).toEqual( + summary.sessions.recent.map((session) => session.key), + ); + }); + it("passes agent scope when listing configured agent session stores", async () => { vi.mocked(listGatewayAgentsBasic).mockReturnValue({ defaultId: "main", diff --git a/src/commands/status.summary.ts b/src/commands/status.summary.ts index c707abfb3ec..03972e09b6f 100644 --- a/src/commands/status.summary.ts +++ b/src/commands/status.summary.ts @@ -180,6 +180,27 @@ function compareSessionCandidatesByUpdatedAt(left: SessionCandidate, right: Sess return (right.updatedAt ?? 0) - (left.updatedAt ?? 0); } +function selectRecentSessionCandidates( + candidates: SessionCandidate[], + limit: number, +): SessionCandidate[] { + const selected: SessionCandidate[] = []; + for (const candidate of candidates) { + const insertAt = selected.findIndex( + (selectedCandidate) => compareSessionCandidatesByUpdatedAt(candidate, selectedCandidate) < 0, + ); + if (insertAt >= 0) { + selected.splice(insertAt, 0, candidate); + if (selected.length > limit) { + selected.pop(); + } + } else if (selected.length < limit) { + selected.push(candidate); + } + } + return selected; +} + function listSessionCandidates(storePath: string, agentId?: string) { return ( listSessionEntries({ @@ -193,7 +214,6 @@ function listSessionCandidates(storePath: string, agentId?: string) { entry, updatedAt: entry?.updatedAt ?? null, })) - .toSorted(compareSessionCandidatesByUpdatedAt) ); } @@ -471,9 +491,10 @@ export async function getStatusSummary( agentList.agents.map(async (agent) => { const storePath = resolveStorePath(cfg.session?.store, { agentId: agent.id }); const candidates = loadSessionCandidates(storePath, agent.id); - const sessions = await buildSessionRows(candidates.slice(0, RECENT_SESSION_LIMIT), { - agentIdOverride: agent.id, - }); + const sessions = await buildSessionRows( + selectRecentSessionCandidates(candidates, RECENT_SESSION_LIMIT), + { agentIdOverride: agent.id }, + ); return { agentId: agent.id, path: storePath, @@ -492,9 +513,10 @@ export async function getStatusSummary( source.storePath, pathCounts.get(source.storePath) === 1 ? source.agentId : undefined, ), - ) - .toSorted((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0)); - const recent = await buildSessionRows(allSessions.slice(0, RECENT_SESSION_LIMIT)); + ); + const recent = await buildSessionRows( + selectRecentSessionCandidates(allSessions, RECENT_SESSION_LIMIT), + ); const totalSessions = allSessions.length; const summary: StatusSummary = {