From 8137d189b07dd00dd7f6a7f9f5c953f32ebb5f88 Mon Sep 17 00:00:00 2001 From: James Long <17031+jlongster@users.noreply.github.com> Date: Wed, 29 Jul 2026 16:36:19 +0000 Subject: [PATCH 1/2] fix(session): sort by latest activity --- packages/app/src/context/server-session.ts | 2 ++ packages/app/src/pages/layout/helpers.test.ts | 25 +++++++++++++++++++ packages/app/src/pages/layout/helpers.ts | 3 ++- packages/core/src/session/projector.ts | 6 +++++ packages/core/test/session-create.test.ts | 24 ++++++++++++++++++ packages/tui/src/context/data.tsx | 2 ++ 6 files changed, 61 insertions(+), 1 deletion(-) diff --git a/packages/app/src/context/server-session.ts b/packages/app/src/context/server-session.ts index 69bec61ba2fc..c4a4a92cf9a4 100644 --- a/packages/app/src/context/server-session.ts +++ b/packages/app/src/context/server-session.ts @@ -944,6 +944,8 @@ export function createServerSession( } const info = data.info[sessionID] + if (event.type === "session.input.admitted" && info) + remember({ ...info, time: { ...info.time, updated: event.created } }) if (event.type === "session.renamed" && info) remember({ ...info, title: event.data.title, time: { ...info.time, updated: event.created } }) if (event.type === "session.moved" && info) diff --git a/packages/app/src/pages/layout/helpers.test.ts b/packages/app/src/pages/layout/helpers.test.ts index df87ddfecd2a..7cf13aac801f 100644 --- a/packages/app/src/pages/layout/helpers.test.ts +++ b/packages/app/src/pages/layout/helpers.test.ts @@ -153,6 +153,31 @@ describe("layout workspace helpers", () => { expect(result?.id).toBe("workspace") }) + test("finds the most recently updated session while sessions are active", () => { + const result = latestRootSession( + [ + { + path: { directory: "/workspace" }, + session: [ + session({ + id: "a", + directory: "/workspace", + time: { created: 110_000, updated: 100_000, archived: undefined }, + }), + session({ + id: "z", + directory: "/workspace", + time: { created: 90_000, updated: 110_000, archived: undefined }, + }), + ], + }, + ], + 120_000, + ) + + expect(result?.id).toBe("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 ce793e282bde..896899bc7c2e 100644 --- a/packages/app/src/pages/layout/helpers.ts +++ b/packages/app/src/pages/layout/helpers.ts @@ -16,7 +16,8 @@ function sortSessions(now: number) { 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 bUpdated - aUpdated || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0) if (aRecent && !bRecent) return -1 if (!aRecent && bRecent) return 1 return bUpdated - aUpdated diff --git a/packages/core/src/session/projector.ts b/packages/core/src/session/projector.ts index 9d0062fee6ae..64ab79dea853 100644 --- a/packages/core/src/session/projector.ts +++ b/packages/core/src/session/projector.ts @@ -657,6 +657,12 @@ const layer = Layer.effectDiscard( input: event.data.input, timeCreated: event.created, }) + yield* db + .update(SessionTable) + .set({ time_updated: DateTime.toEpochMillis(event.created) }) + .where(eq(SessionTable.id, event.data.sessionID)) + .run() + .pipe(Effect.orDie) }), ) yield* bus.project(SessionEvent.Compaction.Admitted, (event) => diff --git a/packages/core/test/session-create.test.ts b/packages/core/test/session-create.test.ts index 1bda9a548813..3a319664972f 100644 --- a/packages/core/test/session-create.test.ts +++ b/packages/core/test/session-create.test.ts @@ -171,6 +171,30 @@ describe("Session.create", () => { }), ) + it.effect("orders sessions by their latest prompt", () => + Effect.gen(function* () { + const session = yield* Session.Service + const { db } = yield* Database.Service + const active = yield* session.create({ location, title: "active" }) + const newer = yield* session.create({ location, title: "newer" }) + + yield* db + .update(SessionTable) + .set({ time_created: -2, time_updated: -2 }) + .where(eq(SessionTable.id, active.id)) + .run() + yield* db + .update(SessionTable) + .set({ time_created: -1, time_updated: -1 }) + .where(eq(SessionTable.id, newer.id)) + .run() + + yield* session.prompt({ sessionID: active.id, text: "continue", resume: false }) + + expect((yield* session.list()).data.map((item) => item.id)).toEqual([active.id, newer.id]) + }), + ) + it.effect("filters direct child sessions by parent ID", () => Effect.gen(function* () { const session = yield* Session.Service diff --git a/packages/tui/src/context/data.tsx b/packages/tui/src/context/data.tsx index 588027579023..7e729b813e50 100644 --- a/packages/tui/src/context/data.tsx +++ b/packages/tui/src/context/data.tsx @@ -404,6 +404,8 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({ break } case "session.input.admitted": + if (store.session.info[event.data.sessionID]) + setStore("session", "info", event.data.sessionID, "time", "updated", event.created) addPending({ id: event.data.inputID, sessionID: event.data.sessionID, From e784dd11969852a43136ecccfcdf9dc478e6cd98 Mon Sep 17 00:00:00 2001 From: James Long <17031+jlongster@users.noreply.github.com> Date: Wed, 29 Jul 2026 16:42:40 +0000 Subject: [PATCH 2/2] chore: scope session sorting to tui --- packages/app/src/context/server-session.ts | 2 -- packages/app/src/pages/layout/helpers.test.ts | 25 ------------------- packages/app/src/pages/layout/helpers.ts | 3 +-- 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/packages/app/src/context/server-session.ts b/packages/app/src/context/server-session.ts index c4a4a92cf9a4..69bec61ba2fc 100644 --- a/packages/app/src/context/server-session.ts +++ b/packages/app/src/context/server-session.ts @@ -944,8 +944,6 @@ export function createServerSession( } const info = data.info[sessionID] - if (event.type === "session.input.admitted" && info) - remember({ ...info, time: { ...info.time, updated: event.created } }) if (event.type === "session.renamed" && info) remember({ ...info, title: event.data.title, time: { ...info.time, updated: event.created } }) if (event.type === "session.moved" && info) diff --git a/packages/app/src/pages/layout/helpers.test.ts b/packages/app/src/pages/layout/helpers.test.ts index 7cf13aac801f..df87ddfecd2a 100644 --- a/packages/app/src/pages/layout/helpers.test.ts +++ b/packages/app/src/pages/layout/helpers.test.ts @@ -153,31 +153,6 @@ describe("layout workspace helpers", () => { expect(result?.id).toBe("workspace") }) - test("finds the most recently updated session while sessions are active", () => { - const result = latestRootSession( - [ - { - path: { directory: "/workspace" }, - session: [ - session({ - id: "a", - directory: "/workspace", - time: { created: 110_000, updated: 100_000, archived: undefined }, - }), - session({ - id: "z", - directory: "/workspace", - time: { created: 90_000, updated: 110_000, archived: undefined }, - }), - ], - }, - ], - 120_000, - ) - - expect(result?.id).toBe("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 896899bc7c2e..ce793e282bde 100644 --- a/packages/app/src/pages/layout/helpers.ts +++ b/packages/app/src/pages/layout/helpers.ts @@ -16,8 +16,7 @@ function sortSessions(now: number) { const bUpdated = b.time.updated ?? b.time.created const aRecent = aUpdated > oneMinuteAgo const bRecent = bUpdated > oneMinuteAgo - if (aRecent && bRecent) - return bUpdated - aUpdated || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0) + 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