From db888d90f82503a33e312e0e58cefb4a85261e21 Mon Sep 17 00:00:00 2001 From: weishu Date: Fri, 19 Dec 2025 15:10:23 +0800 Subject: [PATCH] perf: optimize /api/sessions endpoint response payload Slim down SessionSummary responses to reduce payload size: - Replace full todos array with computed todoProgress summary - Replace metadata object with only necessary fields (name, path, summary.text) - Remove unused fields (thinking, createdAt, permissionMode, modelMode) - Refactor sorting to work on full Session objects before mapping This significantly reduces /api/sessions response size, especially for sessions with many todos or tools. --- server/src/web/routes/sessions.ts | 44 ++++++++++++++++++++---------- web/src/components/SessionList.tsx | 8 ++---- web/src/types/api.ts | 14 ++++++---- 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/server/src/web/routes/sessions.ts b/server/src/web/routes/sessions.ts index 63e3ce0b..a08d1022 100644 --- a/server/src/web/routes/sessions.ts +++ b/server/src/web/routes/sessions.ts @@ -4,31 +4,41 @@ import type { SyncEngine, Session } from '../../sync/syncEngine' import type { WebAppEnv } from '../middleware/auth' import { requireSessionFromParam, requireSyncEngine } from './guards' +type SessionSummaryMetadata = { + name?: string + path: string + summary?: { text: string } +} + type SessionSummary = { id: string active: boolean - thinking: boolean updatedAt: number - createdAt: number - permissionMode: Session['permissionMode'] - modelMode: Session['modelMode'] - metadata: Session['metadata'] - todos?: Session['todos'] + metadata: SessionSummaryMetadata | null + todoProgress: { completed: number; total: number } | null pendingRequestsCount: number } function toSessionSummary(session: Session): SessionSummary { const pendingRequestsCount = session.agentState?.requests ? Object.keys(session.agentState.requests).length : 0 + + const metadata: SessionSummaryMetadata | null = session.metadata ? { + name: session.metadata.name, + path: session.metadata.path, + summary: session.metadata.summary ? { text: session.metadata.summary.text } : undefined + } : null + + const todoProgress = session.todos?.length ? { + completed: session.todos.filter(t => t.status === 'completed').length, + total: session.todos.length + } : null + return { id: session.id, active: session.active, - thinking: session.thinking, updatedAt: session.updatedAt, - createdAt: session.createdAt, - permissionMode: session.permissionMode, - modelMode: session.modelMode, - metadata: session.metadata, - todos: session.todos, + metadata, + todoProgress, pendingRequestsCount } } @@ -50,20 +60,24 @@ export function createSessionsRoutes(getSyncEngine: () => SyncEngine | null): Ho return engine } + const getPendingCount = (s: Session) => s.agentState?.requests ? Object.keys(s.agentState.requests).length : 0 + const sessions = engine.getSessions() - .map(toSessionSummary) .sort((a, b) => { // Active sessions first if (a.active !== b.active) { return a.active ? -1 : 1 } // Within active sessions, sort by pending requests count - if (a.active && a.pendingRequestsCount !== b.pendingRequestsCount) { - return b.pendingRequestsCount - a.pendingRequestsCount + const aPending = getPendingCount(a) + const bPending = getPendingCount(b) + if (a.active && aPending !== bPending) { + return bPending - aPending } // Then by updatedAt return b.updatedAt - a.updatedAt }) + .map(toSessionSummary) return c.json({ sessions }) }) diff --git a/web/src/components/SessionList.tsx b/web/src/components/SessionList.tsx index 47fa34c9..1ee38413 100644 --- a/web/src/components/SessionList.tsx +++ b/web/src/components/SessionList.tsx @@ -58,11 +58,9 @@ function getSessionTitle(session: SessionSummary): string { } function getTodoProgress(session: SessionSummary): { completed: number; total: number } | null { - if (!session.todos || session.todos.length === 0) return null - const total = session.todos.length - const completed = session.todos.filter(t => t.status === 'completed').length - if (completed === total) return null - return { completed, total } + if (!session.todoProgress) return null + if (session.todoProgress.completed === session.todoProgress.total) return null + return session.todoProgress } export function SessionList(props: { diff --git a/web/src/types/api.ts b/web/src/types/api.ts index 531f2ce8..ed860803 100644 --- a/web/src/types/api.ts +++ b/web/src/types/api.ts @@ -58,16 +58,18 @@ export type Session = { modelMode?: ModelMode } +export type SessionSummaryMetadata = { + name?: string + path: string + summary?: { text: string } +} + export type SessionSummary = { id: string active: boolean - thinking: boolean updatedAt: number - createdAt: number - permissionMode: PermissionMode - modelMode: ModelMode - metadata: SessionMetadataSummary | null - todos?: TodoItem[] + metadata: SessionSummaryMetadata | null + todoProgress: { completed: number; total: number } | null pendingRequestsCount: number }