From 28bcc0e4f4d4679946542e05412cb96d737a0428 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2026 04:23:04 +0000 Subject: [PATCH] fix(app): sort sessions by persisted time (#41000) Co-authored-by: Dax --- .../pages/home/home-sessions-controller.tsx | 4 +-- packages/app/src/pages/layout/helpers.test.ts | 26 +++++++++++++++++++ packages/app/src/pages/layout/helpers.ts | 22 +++++----------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/packages/app/src/pages/home/home-sessions-controller.tsx b/packages/app/src/pages/home/home-sessions-controller.tsx index c0ff0c88ae5..25d896393ca 100644 --- a/packages/app/src/pages/home/home-sessions-controller.tsx +++ b/packages/app/src/pages/home/home-sessions-controller.tsx @@ -15,7 +15,7 @@ import type { LocalProject } from "@/context/layout" import { useLanguage } from "@/context/language" import { ServerConnection } from "@/context/server" import { sessionHasOpenTab, useTabs } from "@/context/tabs" -import { displayName, errorMessage, projectForSession } from "@/pages/layout/helpers" +import { compareSessionTime, displayName, errorMessage, projectForSession } from "@/pages/layout/helpers" import { useSessionTabAvatarState } from "@/pages/layout/project-avatar-state" import { pathKey } from "@/utils/path-key" import { showToast } from "@/utils/toast" @@ -254,7 +254,7 @@ function buildHomeSessionRecords(input: { const directories = new Set(input.projectDirectories().map(pathKey)) const sessions = input.sessions().filter((session) => directories.has(pathKey(session.directory))) return [...new Map(sessions.map((session) => [session.id, session] as const)).values()] - .sort((a, b) => (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created)) + .sort(compareSessionTime) .flatMap((session) => { const directory = pathKey(session.directory) const project = diff --git a/packages/app/src/pages/layout/helpers.test.ts b/packages/app/src/pages/layout/helpers.test.ts index df87ddfecd2..cb58b6f8b6d 100644 --- a/packages/app/src/pages/layout/helpers.test.ts +++ b/packages/app/src/pages/layout/helpers.test.ts @@ -10,6 +10,7 @@ import { type Session } from "@opencode-ai/sdk/v2/client" import { childSessionOnPath, closeHomeProject, + compareSessionTime, displayName, effectiveWorkspaceOrder, errorMessage, @@ -18,6 +19,7 @@ import { homeProjectDirectories, homeSessionServerStatus, latestRootSession, + sortedRootSessions, toggleHomeProjectSelection, } from "./helpers" import { pathKey } from "@/utils/path-key" @@ -153,6 +155,30 @@ describe("layout workspace helpers", () => { expect(result?.id).toBe("workspace") }) + test("sorts recent sessions by persisted update time instead of id", () => { + const result = sortedRootSessions( + { + path: { directory: "/workspace" }, + session: [ + session({ id: "ses_z", directory: "/workspace", time: { created: 1, updated: 2, archived: undefined } }), + session({ id: "ses_a", directory: "/workspace", time: { created: 1, updated: 3, archived: undefined } }), + ], + }, + 3, + ) + + expect(result.map((item) => item.id)).toEqual(["ses_a", "ses_z"]) + }) + + test("uses id only to break equal session timestamps", () => { + const sessions = [ + session({ id: "ses_z", directory: "/workspace", time: { created: 1, updated: 2, archived: undefined } }), + session({ id: "ses_a", directory: "/workspace", time: { created: 1, updated: 2, archived: undefined } }), + ] + + expect(sessions.sort(compareSessionTime).map((item) => item.id)).toEqual(["ses_a", "ses_z"]) + }) + test("detects project permissions with a filter", () => { const result = hasProjectPermissions( { diff --git a/packages/app/src/pages/layout/helpers.ts b/packages/app/src/pages/layout/helpers.ts index ce793e282bd..5245ba6f16d 100644 --- a/packages/app/src/pages/layout/helpers.ts +++ b/packages/app/src/pages/layout/helpers.ts @@ -9,18 +9,10 @@ type SessionStore = { path: { directory: string } } -function sortSessions(now: number) { - const oneMinuteAgo = now - 60 * 1000 - return (a: Session, b: Session) => { - const aUpdated = a.time.updated ?? a.time.created - const bUpdated = b.time.updated ?? b.time.created - const aRecent = aUpdated > oneMinuteAgo - const bRecent = bUpdated > oneMinuteAgo - if (aRecent && bRecent) return a.id < b.id ? -1 : a.id > b.id ? 1 : 0 - if (aRecent && !bRecent) return -1 - if (!aRecent && bRecent) return 1 - return bUpdated - aUpdated - } +export function compareSessionTime(a: Session, b: Session) { + const updated = (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created) + if (updated !== 0) return updated + return a.id < b.id ? -1 : a.id > b.id ? 1 : 0 } const isRootVisibleSession = (session: Session, directory: string) => @@ -29,10 +21,10 @@ const isRootVisibleSession = (session: Session, directory: string) => export const roots = (store: SessionStore) => (store.session ?? []).filter((session) => isRootVisibleSession(session, store.path.directory)) -export const sortedRootSessions = (store: SessionStore, now: number) => roots(store).sort(sortSessions(now)) +export const sortedRootSessions = (store: SessionStore, _now: number) => roots(store).sort(compareSessionTime) -export const latestRootSession = (stores: SessionStore[], now: number) => - stores.flatMap(roots).sort(sortSessions(now))[0] +export const latestRootSession = (stores: SessionStore[], _now: number) => + stores.flatMap(roots).sort(compareSessionTime)[0] export function hasProjectPermissions( request: Record | undefined,