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.
This commit is contained in:
weishu
2025-12-19 15:10:23 +08:00
parent bb514482a2
commit db888d90f8
3 changed files with 40 additions and 26 deletions
+29 -15
View File
@@ -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 })
})
+3 -5
View File
@@ -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: {
+8 -6
View File
@@ -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
}