diff --git a/packages/app/src/context/global-sync/session-trim.ts b/packages/app/src/context/global-sync/session-trim.ts index 800ba74a68..ba13cb5ec0 100644 --- a/packages/app/src/context/global-sync/session-trim.ts +++ b/packages/app/src/context/global-sync/session-trim.ts @@ -41,6 +41,7 @@ export function trimSessions( .filter((s) => !s.time?.archived) .sort((a, b) => cmp(a.id, b.id)) const roots = all.filter((s) => !s.parentID) + roots.sort(compareSessionRecent) const children = all.filter((s) => !!s.parentID) const base = roots.slice(0, limit) const recent = takeRecentSessions(roots.slice(limit), SESSION_RECENT_LIMIT, cutoff) diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/session.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/session.ts index 275fa2956c..b0038076a1 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/session.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/v2/session.ts @@ -1,6 +1,6 @@ import { WorkspaceID } from "@/control-plane/schema" import { SessionV2 } from "@/v2/session" -import { Effect, Schema } from "effect" +import { DateTime, Effect, Schema } from "effect" import { HttpApiBuilder, HttpApiError, HttpApiSchema } from "effect/unstable/httpapi" import { InstanceHttpApi } from "../../api" @@ -55,7 +55,7 @@ const sessionCursor = { filters: Pick, ) { return Buffer.from( - JSON.stringify({ id: session.id, time: session.time.created, order, direction, ...filters }), + JSON.stringify({ ...filters, id: session.id, time: DateTime.toEpochMillis(session.time.updated), order, direction }), ).toString("base64url") }, decode(input: string) { diff --git a/packages/opencode/src/v2/session.ts b/packages/opencode/src/v2/session.ts index a3c386f66d..ec67d1820a 100644 --- a/packages/opencode/src/v2/session.ts +++ b/packages/opencode/src/v2/session.ts @@ -177,6 +177,8 @@ export const layer = Layer.effect( list: Effect.fn("V2Session.list")(function* (input) { const direction = input.cursor?.direction ?? "next" let order = input.order ?? "desc" + // This is a load bearing sort, desktop relies on this + const sortColumn = SessionTable.time_updated // Query the adjacent rows in reverse, then flip them back into the requested order below. if (direction === "previous" && order === "asc") order = "desc" if (direction === "previous" && order === "desc") order = "asc" @@ -186,18 +188,18 @@ export const layer = Layer.effect( conditions.push(or(eq(SessionTable.path, input.path), like(SessionTable.path, `${input.path}/%`))!) if (input.workspaceID) conditions.push(eq(SessionTable.workspace_id, input.workspaceID)) if (input.roots) conditions.push(isNull(SessionTable.parent_id)) - if (input.start) conditions.push(gte(SessionTable.time_created, input.start)) + if (input.start) conditions.push(gte(sortColumn, input.start)) if (input.search) conditions.push(like(SessionTable.title, `%${input.search}%`)) if (input.cursor) { conditions.push( order === "asc" ? or( - gt(SessionTable.time_created, input.cursor.time), - and(eq(SessionTable.time_created, input.cursor.time), gt(SessionTable.id, input.cursor.id)), + gt(sortColumn, input.cursor.time), + and(eq(sortColumn, input.cursor.time), gt(SessionTable.id, input.cursor.id)), )! : or( - lt(SessionTable.time_created, input.cursor.time), - and(eq(SessionTable.time_created, input.cursor.time), lt(SessionTable.id, input.cursor.id)), + lt(sortColumn, input.cursor.time), + and(eq(sortColumn, input.cursor.time), lt(SessionTable.id, input.cursor.id)), )!, ) } @@ -206,7 +208,7 @@ export const layer = Layer.effect( .from(SessionTable) .where(conditions.length > 0 ? and(...conditions) : undefined) .orderBy( - order === "asc" ? asc(SessionTable.time_created) : desc(SessionTable.time_created), + order === "asc" ? asc(sortColumn) : desc(sortColumn), order === "asc" ? asc(SessionTable.id) : desc(SessionTable.id), )