From 19f327a48cf9f0623dea17d61c076f09285f0ddb Mon Sep 17 00:00:00 2001 From: kumarabhirup Date: Thu, 19 Feb 2026 21:51:55 -0800 Subject: [PATCH] web: auto-discover orphaned session files in web-sessions index --- apps/web/app/api/web-sessions/route.ts | 65 +++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/apps/web/app/api/web-sessions/route.ts b/apps/web/app/api/web-sessions/route.ts index 7c2de3e1f1a..204e81756ff 100644 --- a/apps/web/app/api/web-sessions/route.ts +++ b/apps/web/app/api/web-sessions/route.ts @@ -1,4 +1,4 @@ -import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs"; +import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from "node:fs"; import { join } from "node:path"; import { randomUUID } from "node:crypto"; import { resolveWebChatDir } from "@/lib/workspace"; @@ -23,15 +23,68 @@ function ensureDir() { return dir; } +/** + * Read the session index, auto-discovering any orphaned .jsonl files + * that aren't in the index (e.g. from profile switches or missing index). + */ function readIndex(): WebSessionMeta[] { const dir = ensureDir(); const indexFile = join(dir, "index.json"); - if (!existsSync(indexFile)) {return [];} - try { - return JSON.parse(readFileSync(indexFile, "utf-8")); - } catch { - return []; + let index: WebSessionMeta[] = []; + if (existsSync(indexFile)) { + try { + index = JSON.parse(readFileSync(indexFile, "utf-8")); + } catch { + index = []; + } } + + // Scan for orphaned .jsonl files not in the index + try { + const indexed = new Set(index.map((s) => s.id)); + const files = readdirSync(dir).filter((f) => f.endsWith(".jsonl")); + let dirty = false; + for (const file of files) { + const id = file.replace(/\.jsonl$/, ""); + if (indexed.has(id)) {continue;} + + // Build a minimal index entry from the file + const fp = join(dir, file); + const stat = statSync(fp); + let title = "New Chat"; + let messageCount = 0; + try { + const content = readFileSync(fp, "utf-8"); + const lines = content.split("\n").filter((l) => l.trim()); + messageCount = lines.length; + // Try to extract a title from the first user message + for (const line of lines) { + const parsed = JSON.parse(line); + if (parsed.role === "user" && parsed.content) { + const text = String(parsed.content); + title = text.length > 60 ? text.slice(0, 60) + "..." : text; + break; + } + } + } catch { /* best-effort */ } + + index.push({ + id, + title, + createdAt: stat.birthtimeMs || stat.mtimeMs, + updatedAt: stat.mtimeMs, + messageCount, + }); + dirty = true; + } + + if (dirty) { + index.sort((a, b) => b.updatedAt - a.updatedAt); + writeFileSync(indexFile, JSON.stringify(index, null, 2)); + } + } catch { /* best-effort */ } + + return index; } function writeIndex(sessions: WebSessionMeta[]) {