feat(sessions): show global recent sessions without picking a directory (#144)

session.list() now fetches GET /experimental/session (all sessions across
every directory) and falls back to the legacy directory-scoped GET /session
only on 404 (older servers). A directory-less /session is directory-scoped and
returns [] when the active dir has no sessions, so the Recent Sessions list was
empty unless the user first picked a folder.

Global shaping (roots filter, title search, sort by time.updated desc, limit)
moved to a pure, unit-tested src/lib/session-list.ts (no expo/fetch import) with
10 node --test cases.

Co-authored-by: dzianisv <engineer@gray-knight-m1.local>
This commit is contained in:
Den
2026-07-23 00:08:43 -07:00
committed by GitHub
parent c9b504711e
commit bc1dbcf0cd
3 changed files with 228 additions and 8 deletions

View File

@@ -6,6 +6,7 @@ import { fetch as expoFetch } from "expo/fetch"
import { buildRequestHeaders } from "./headers"
import { SSEParser } from "./sse"
import { apiErrorFor } from "./api-error"
import { loadSessionList } from "./session-list"
import type { FileRoot } from "./file-roots"
export { ApiAuthError, isAuthError } from "./api-error"
@@ -342,14 +343,33 @@ export function createClient(config: ClientConfig) {
},
session: {
list: (params?: { roots?: boolean; limit?: number; search?: string }) => {
const query = new URLSearchParams()
if (params?.roots) query.set("roots", "true")
if (params?.limit) query.set("limit", String(params.limit))
if (params?.search) query.set("search", params.search)
const qs = query.toString()
return request<Session[]>(config, `/session${qs ? `?${qs}` : ""}`)
},
// Prefer the GLOBAL experimental endpoint (all sessions across every
// directory) so the Recent Sessions list works without the user first
// picking a folder — a directory-less GET /session is directory-scoped
// and returns [] on servers whose active dir has no sessions. Shaping
// (roots filter, search, sort-by-updated, limit) happens client-side in
// loadSessionList; we fetch /experimental/session with no query params
// because the server applies `limit` before we can filter to roots.
// Falls back to the legacy /session path only on 404 (older servers).
list: (params?: { roots?: boolean; limit?: number; search?: string }): Promise<Session[]> =>
loadSessionList(
{
getExperimental: async (): Promise<Session[] | null> => {
const response = await fetchWithTimeout(`${config.baseUrl}/experimental/session`, {
headers: createHeaders(config),
})
// Older servers lack this route — signal fallback to legacy /session.
if (response.status === 404) return null
if (!response.ok) {
const body = await response.text()
throw apiErrorFor(response.status, `API Error: ${response.status} - ${body}`)
}
return response.json()
},
getLegacy: (query) => request<Session[]>(config, `/session${query}`),
},
params,
),
get: (sessionID: string) => request<Session>(config, `/session/${sessionID}`),