feat(history): pin sessions to the top of the history tab (#886)

- Hold-menu Pin/Unpin action on session cards (PATCH /api/sessions/{id}
  with {pinned}, verified against the backend SessionRename model)
- Pinned sessions sort to the top on every load (client-side stable sort
  — the backend back-fills pins into page 1 but does not lift them)
- Pin indicator on pinned cards; search-result cards omit the pin action
- Tests: pin-first sort on load, toggle success (moves to top), failure
  (order kept + toast)

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-08-12 14:32:48 +03:00
1 parent 95178d3438
commit 685b870cad
6 files changed
+208 -19

No files matched your search

@@ -23,6 +23,9 @@ data class SessionInfo(
val display_name: String? = null,
val model: String? = null,
val terminal_backend: String? = null,
// Durable "keep" flag: pinned sessions are exempt from the auto-archive
// sweep and are surfaced first in the history list (sorted client-side).
val pinned: Boolean? = null,
)
@Serializable
@@ -35,7 +38,10 @@ data class SessionStatsResponse(
@Serializable
data class SessionRenameRequest(
val title: String,
// Both fields are optional: the backend PATCH accepts any subset
// (SessionRename model: title/archived/pinned, omitted = unchanged).
val title: String? = null,
val pinned: Boolean? = null,
)
@Serializable
@@ -207,6 +207,14 @@ interface HermesApiService {
@Body body: SessionRenameRequest,
): Response<Unit>
// Pin/unpin rides the same PATCH /api/sessions/{id} — body carries only
// {pinned} (backend SessionRename model, any subset accepted).
@PATCH("api/sessions/{id}")
suspend fun setSessionPinned(
@Path("id", encoded = true) sessionId: String,
@Body body: SessionRenameRequest,
): Response<Unit>
@POST("api/sessions/bulk-delete")
suspend fun bulkDeleteSessions(
@Body body: BulkDeleteRequest,