feat: JSONL title cache + live/project session sync - #17
Conversation
- Extract isMac to shared utils/platform.ts (was duplicated in App.tsx + useTerminal.ts) - Track and clean up all timers (scrollTimer, nudgeTimer, reconnectTimer) on dispose and reconnect - Close stale WebSocket before creating new one in connect() — prevents orphaned connections - Only auto-scroll to bottom when user is already at bottom — don't fight manual scrollback - Move MAX_RETRY_DELAY to module level - Make case "b" call toggleSidebar() directly instead of relying on event bubbling - Add nudgeResize for TUI redraw on reconnect, with tracked timer - Increase scrollback to 10k lines, server buffer to 1MB - Replace O(n²) Array.shift() buffer trim with bulk splice() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Claude Agent SDK's listSessions() reads only 64KB head/tail of session JSONL files, causing custom titles to disappear as sessions grow. This adds a server-side title cache that reads the actual files with mtime-based invalidation, plus syncs live session names with project session titles and shows green status dots on project sessions that have active live sessions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix N+1 resolveProjectDir: group by cwd, resolve each once - Eliminate double stat: single file handle for cache check + read - Sidebar flatMap O(L*P) → useMemo Map lookup O(1) - Prune orphaned cache entries on each batch call - Extract title marker constants, skip already-scanned regions in phase 3 - Stream scan now only reads the middle gap [head, size-tail] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| if (title) { | ||
| cache.set(sessionId, { title, mtimeMs }); | ||
| } else { | ||
| cache.delete(sessionId); |
There was a problem hiding this comment.
🟡 Warning
Problem: cache.delete(sessionId) when extractTitle returns null means sessions with no custom title are re-scanned from disk on every poll cycle (every 30s). The mtime check on line 233 only short-circuits when a CacheEntry exists, so untitled sessions never get one.
Why it matters: In a project with many sessions that have never been renamed (common for exploratory sessions), every /api/projects poll opens, stats, and reads up to 512KB from each of those JSONL files. Even with OS page-cache, the repeated fh.stat() + buffer allocation adds up. A user with 50 untitled sessions burns ~150 syscalls + 25MB of buffer allocations per 30s poll.
Suggested fix:
// Store a sentinel so mtime-validated no-title is also cached
cache.set(sessionId, { title: "", mtimeMs });Then in the early-return check:
const cached = cache.get(sessionId);
if (cached && cached.mtimeMs === mtimeMs) {
return cached.title || null; // "" → null, title → title
}Also update batchGetTitles cache-check path to skip calling getCachedTitle when the mtime-matched entry is empty (title won't have appeared without an mtime change, so the cache hit is still valid).
nox-0x
left a comment
There was a problem hiding this comment.
Solid implementation — the tail-first + mtime-cached JSONL scanning is the right approach to work around the SDK limitation, and the three-phase extraction logic correctly handles the head/middle/tail coverage for arbitrarily large files. The useMemo Sidebar optimization and buffer-trimming O(n²)→splice fix are both good cleanup. One warning flagged inline: sessions without custom titles bypass the mtime cache and get re-scanned from disk every poll — store a sentinel empty string in CacheEntry to fix. Not a blocker for merging.
Summary
customTitle(64KB head/tail buffer limitation). Reads actual JSONL files with mtime-based caching and efficient tail-first scanning./renamed in Claude CodeDetails
The SDK's
listSessions()reads only 64KB head/tail of each session JSONL file. When sessions grow,customTitleentries fall outside that window and disappear. This is a well-known bug with 12+ linked issues and no fix from Anthropic yet.Resolution order:
SDK customTitle → mtime-validated cache → JSONL parse → SDK summaryTest plan
/api/projectsreturns sessions/renamea session in Claude Code, verify title appears in dashboard within 30s/rename🤖 Generated with Claude Code