feat: implement session rename with SQLite storage#413
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: Repository UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (19)
📝 WalkthroughWalkthroughAdds DB-backed, provider-aware session naming: new Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Frontend
participant API as "Client API\n(src/utils/api.js)"
participant Server
participant DBAPI as "sessionNamesDb\n(server/database/db.js)"
participant DB
User->>Frontend: Edit session title + Save (provider)
Frontend->>API: renameSession(sessionId, summary, provider)
API->>Server: PUT /api/sessions/:sessionId/rename {summary, provider}
Server->>DBAPI: sessionNamesDb.setName(sessionId, provider, summary)
DBAPI->>DB: INSERT ... ON CONFLICT UPDATE custom_name, updated_at
DB-->>DBAPI: OK
DBAPI-->>Server: OK
Server-->>API: { success: true }
API-->>Frontend: success
Frontend->>Server: GET /api/projects/:projectName/sessions
Server->>DBAPI: sessionNamesDb.getNames([sessionIds], provider)
DBAPI->>DB: SELECT session_id, custom_name FROM session_names WHERE session_id IN (...) AND provider=...
DB-->>DBAPI: rows
DBAPI-->>Server: Map(sessionId -> custom_name)
Server->>Server: applyCustomSessionNames(sessions, provider)
Server-->>Frontend: sessions (with custom_name applied to session.summary)
Frontend-->>User: render renamed sessions
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (5)
server/routes/codex.js (1)
63-71: Duplicate custom-name injection pattern — consider extracting a shared helper.The same 6-line block (collect IDs →
getNames→ assignsummary) appears identically incursor.jsand, per the AI summaries, also inserver/index.jsandserver/projects.js. Extracting it into a small utility (e.g.,applyCustomSessionNames(sessions, provider)exported fromdb.jsor a shared utils module) would reduce repetition.♻️ Suggested helper
// server/database/db.js (add alongside sessionNamesDb) +export function applyCustomSessionNames(sessions, provider) { + if (!sessions?.length) return; + const ids = sessions.map(s => s.id); + const customNames = sessionNamesDb.getNames(ids, provider); + for (const session of sessions) { + const custom = customNames.get(session.id); + if (custom) session.summary = custom; + } +}// server/routes/codex.js -import { sessionNamesDb } from '../database/db.js'; +import { applyCustomSessionNames } from '../database/db.js'; ... - // Apply custom session names from DB - if (sessions?.length > 0) { - const ids = sessions.map(s => s.id); - const customNames = sessionNamesDb.getNames(ids, 'codex'); - for (const session of sessions) { - const custom = customNames.get(session.id); - if (custom) session.summary = custom; - } - } + applyCustomSessionNames(sessions, 'codex');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/routes/codex.js` around lines 63 - 71, The repeated pattern that collects session ids, calls sessionNamesDb.getNames(ids, 'codex'), and injects the returned name into session.summary should be extracted into a single helper (e.g., applyCustomSessionNames(sessions, provider)) exported from a shared module (db.js or utils). Implement applyCustomSessionNames to accept the sessions array and provider string, early-return if sessions is empty, map ids, call sessionNamesDb.getNames(ids, provider), and assign custom names to each session.summary; then replace the duplicate blocks in codex.js, cursor.js, server/index.js and server/projects.js with a call to applyCustomSessionNames(sessions, 'codex' /* or appropriate provider */).server/projects.js (1)
568-568: Dead call —project.sessionsis always[]here.For manual projects,
project.sessionsis initialized to[](line 559) and no Claude sessions are ever populated before this call.applyCustomSessionNamesshort-circuits on the empty guard and is a no-op. Consider removing it.♻️ Proposed fix
- applyCustomSessionNames(project.sessions, 'claude'); - // Try to fetch Cursor sessions for manual projects too🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/projects.js` at line 568, The call applyCustomSessionNames(project.sessions, 'claude') is dead because project.sessions is initialized to [] for manual projects and never populated before this call; remove the no-op invocation (delete the applyCustomSessionNames(...) line) or move it to a place after sessions are actually populated (where project.sessions may be non-empty) — locate the call to applyCustomSessionNames and either remove it or relocate it to the code path that fills project.sessions so the function can run on a non-empty array.src/components/sidebar/hooks/useSidebarController.ts (1)
404-404:projectNameis declared but never used in the function body.The rename API only needs
sessionIdandprovider. If the parameter is kept for interface symmetry withonDeleteSession, prefix it with_to communicate the intent and satisfynoUnusedParameters.♻️ Proposed fix
- async (projectName: string, sessionId: string, summary: string, provider: SessionProvider) => { + async (_projectName: string, sessionId: string, summary: string, provider: SessionProvider) => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/sidebar/hooks/useSidebarController.ts` at line 404, The async rename handler declared as async (projectName: string, sessionId: string, summary: string, provider: SessionProvider) has an unused parameter projectName; either remove it or rename it to _projectName to satisfy noUnusedParameters while keeping interface symmetry with onDeleteSession. Update the function signature in useSidebarController's rename handler (the async arrow function) to use _projectName if you must preserve the parameter, or delete projectName and adjust any callers/typing to only accept sessionId, summary, and provider.server/index.js (2)
549-554:sessionIdis unvalidated andsummaryhas no length cap.The token-usage endpoint at Line 1639 sanitizes
sessionIdwithString(sessionId).replace(/[^a-zA-Z0-9._-]/g, '')before use. The rename endpoint skips this step entirely, which is inconsistent. While parameterized queries prevent SQL injection, applying the same sanitization pattern would keep the two code paths consistent. Similarly, a missing upper-bound onsummaryallows arbitrarily long strings to be stored.♻️ Proposed fix
const { sessionId } = req.params; + const safeSessionId = String(sessionId).replace(/[^a-zA-Z0-9._-]/g, ''); + if (!safeSessionId) { + return res.status(400).json({ error: 'Invalid sessionId' }); + } const { summary, provider } = req.body; - if (!summary || typeof summary !== 'string' || summary.trim() === '') { + const MAX_SUMMARY_LENGTH = 500; + if (!summary || typeof summary !== 'string' || summary.trim() === '') { return res.status(400).json({ error: 'Summary is required' }); } + if (summary.trim().length > MAX_SUMMARY_LENGTH) { + return res.status(400).json({ error: `Summary must not exceed ${MAX_SUMMARY_LENGTH} characters` }); + } ... - sessionNamesDb.setName(sessionId, provider, summary.trim()); + sessionNamesDb.setName(safeSessionId, provider, summary.trim());🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/index.js` around lines 549 - 554, The rename endpoint (app.put('/api/sessions/:sessionId/rename')) is missing the same sessionId sanitization used elsewhere and allows unbounded summary lengths; update the handler to sanitize sessionId using String(sessionId).replace(/[^a-zA-Z0-9._-]/g, '') before any DB use (replace uses of sessionId with the sanitized variable) and validate summary to be a non-empty trimmed string with an enforced max length (e.g., 200 characters), returning res.status(400).json({ error: '...' }) when validation fails.
556-558: Validateprovideragainst known values.Any non-empty string passes the current check, so callers can persist entries like
"foobar"that will never be queried, pollutingsession_names. Restrict to the three supported values.♻️ Proposed fix
+ const VALID_PROVIDERS = ['claude', 'codex', 'cursor']; if (!provider || typeof provider !== 'string') { return res.status(400).json({ error: 'Provider is required' }); } + if (!VALID_PROVIDERS.includes(provider)) { + return res.status(400).json({ error: `Provider must be one of: ${VALID_PROVIDERS.join(', ')}` }); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/index.js` around lines 556 - 558, The current check around the provider variable (the if (!provider || typeof provider !== 'string') block) allows any non-empty string; change it to validate provider against the explicit set of supported values by replacing that condition with a membership check (e.g. create an allowedProviders array of the three supported provider strings and verify allowedProviders.includes(provider)); if the value is not in the allowed set, return res.status(400).json with an error message that lists the allowed providers so only the three supported values are accepted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@server/database/db.js`:
- Around line 79-92: The empty catch around the db.exec calls for creating
session_names and idx_session_names_lookup is swallowing real DB errors; remove
the inner try/catch so failures from db.exec(...) (creating session_names /
creating idx_session_names_lookup) propagate to the outer runMigrations error
handling, or if you prefer to keep a local catch, change it to catch (err) and
rethrow or log the error with context (e.g., include err and mention
"session_names/index creation") instead of silently ignoring it.
In `@server/projects.js`:
- Around line 71-79: The call to sessionNamesDb.getNames inside
applyCustomSessionNames can throw and currently bubbles up; wrap the getNames
call (or the whole body of applyCustomSessionNames) in a try/catch so DB errors
are caught, logged, and do not abort getProjects; if an error occurs, log via
the existing logger (or console.error) and simply return without modifying
sessions so processing continues. Ensure you reference applyCustomSessionNames
and sessionNamesDb.getNames when locating the change.
In `@server/routes/cursor.js`:
- Around line 568-570: getSessionName currently ignores Cursor session.summary
and always returns session.name; update the Cursor branch in
src/components/sidebar/utils/utils.ts inside the getSessionName function so it
returns session.summary || session.name || t('projects.untitledSession') instead
of session.name || t('projects.untitledSession'), ensuring custom renames are
used; locate the Cursor case (the branch that handles Cursor sessions) and
replace the return expression accordingly.
In `@src/components/sidebar/hooks/useSidebarController.ts`:
- Around line 403-423: updateSessionSummary currently clears edit state
(setEditingSession, setEditingSessionName) before awaiting api.renameSession,
which removes the input on failure; change it to perform those clears in a
finally block after the API call/logic (like saveProjectName does).
Specifically, in updateSessionSummary (the async callback that calls
api.renameSession and onRefresh), remove the synchronous
setEditingSession(null)/setEditingSessionName('') at the top and instead call
them inside a finally clause after the try/catch so the UI only clears when the
operation completes (success or failure), keeping api.renameSession, onRefresh
and the existing error handling intact.
---
Nitpick comments:
In `@server/index.js`:
- Around line 549-554: The rename endpoint
(app.put('/api/sessions/:sessionId/rename')) is missing the same sessionId
sanitization used elsewhere and allows unbounded summary lengths; update the
handler to sanitize sessionId using
String(sessionId).replace(/[^a-zA-Z0-9._-]/g, '') before any DB use (replace
uses of sessionId with the sanitized variable) and validate summary to be a
non-empty trimmed string with an enforced max length (e.g., 200 characters),
returning res.status(400).json({ error: '...' }) when validation fails.
- Around line 556-558: The current check around the provider variable (the if
(!provider || typeof provider !== 'string') block) allows any non-empty string;
change it to validate provider against the explicit set of supported values by
replacing that condition with a membership check (e.g. create an
allowedProviders array of the three supported provider strings and verify
allowedProviders.includes(provider)); if the value is not in the allowed set,
return res.status(400).json with an error message that lists the allowed
providers so only the three supported values are accepted.
In `@server/projects.js`:
- Line 568: The call applyCustomSessionNames(project.sessions, 'claude') is dead
because project.sessions is initialized to [] for manual projects and never
populated before this call; remove the no-op invocation (delete the
applyCustomSessionNames(...) line) or move it to a place after sessions are
actually populated (where project.sessions may be non-empty) — locate the call
to applyCustomSessionNames and either remove it or relocate it to the code path
that fills project.sessions so the function can run on a non-empty array.
In `@server/routes/codex.js`:
- Around line 63-71: The repeated pattern that collects session ids, calls
sessionNamesDb.getNames(ids, 'codex'), and injects the returned name into
session.summary should be extracted into a single helper (e.g.,
applyCustomSessionNames(sessions, provider)) exported from a shared module
(db.js or utils). Implement applyCustomSessionNames to accept the sessions array
and provider string, early-return if sessions is empty, map ids, call
sessionNamesDb.getNames(ids, provider), and assign custom names to each
session.summary; then replace the duplicate blocks in codex.js, cursor.js,
server/index.js and server/projects.js with a call to
applyCustomSessionNames(sessions, 'codex' /* or appropriate provider */).
In `@src/components/sidebar/hooks/useSidebarController.ts`:
- Line 404: The async rename handler declared as async (projectName: string,
sessionId: string, summary: string, provider: SessionProvider) has an unused
parameter projectName; either remove it or rename it to _projectName to satisfy
noUnusedParameters while keeping interface symmetry with onDeleteSession. Update
the function signature in useSidebarController's rename handler (the async arrow
function) to use _projectName if you must preserve the parameter, or delete
projectName and adjust any callers/typing to only accept sessionId, summary, and
provider.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
server/database/db.js (1)
379-392:getNamesdefeats the prepared-statement cache and has a theoretical parameter-count ceiling.Because the SQL string varies with
sessionIds.length(each different count is a distinct string),db.prepare()produces a cache miss on every call with a new batch size. Additionally, spreading...sessionIds, providerinto.all()works only whilesessionIds.length < SQLITE_MAX_VARIABLE_NUMBER(32766 with the bundled SQLite 3.46+); exceeding that would throw, andapplyCustomSessionNames's catch would silently discard all custom names for the affected batch.Consider chunking into fixed-size batches (e.g., 500) with a stable prepared statement per chunk:
♻️ Suggested refactor
+ const CHUNK_SIZE = 500; + const FETCH_STMT = db.prepare( + `SELECT session_id, custom_name FROM session_names + WHERE session_id IN (${Array(CHUNK_SIZE).fill('?').join(',')}) AND provider = ?` + ); + getNames: (sessionIds, provider) => { try { if (!sessionIds.length) return new Map(); - const placeholders = sessionIds.map(() => '?').join(','); - const rows = db.prepare( - `SELECT session_id, custom_name FROM session_names - WHERE session_id IN (${placeholders}) AND provider = ?` - ).all(...sessionIds, provider); + const result = new Map(); + for (let i = 0; i < sessionIds.length; i += CHUNK_SIZE) { + const chunk = sessionIds.slice(i, i + CHUNK_SIZE); + const stmt = chunk.length === CHUNK_SIZE + ? FETCH_STMT + : db.prepare( + `SELECT session_id, custom_name FROM session_names + WHERE session_id IN (${chunk.map(() => '?').join(',')}) AND provider = ?` + ); + const rows = stmt.all(...chunk, provider); + rows.forEach(r => result.set(r.session_id, r.custom_name)); + } - return new Map(rows.map(r => [r.session_id, r.custom_name])); + return result; } catch (err) { throw err; } },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/database/db.js` around lines 379 - 392, getNames currently builds a different SQL string for every batch size and spreads sessionIds into .all(), which busts the prepare cache and can exceed SQLITE_MAX_VARIABLE_NUMBER; change getNames to process sessionIds in fixed-size chunks (e.g., 500) and query with a stable prepared statement that accepts a single JSON array parameter (e.g., "WHERE session_id IN (SELECT value FROM json_each(?)) AND provider = ?"), calling .all(JSON.stringify(chunk), provider) for each chunk and merging results; update any callers like applyCustomSessionNames to rely on the new getNames behavior so large batches are handled without cache misses or parameter-count errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@server/index.js`:
- Around line 544-547: The code currently creates safeSessionId by stripping
disallowed chars and only rejects when empty, which can silently change a
valid-but-different session ID; instead validate that the sanitized value
exactly matches the original sessionId and return 400 on mismatch. In the
handler that computes safeSessionId (the sessionId param and safeSessionId
variable), compare String(sessionId) to the sanitized string and if they differ
respond with res.status(400).json({ error: 'Invalid sessionId' }) so no
renaming/lookup happens on a mutated ID.
---
Duplicate comments:
In `@server/database/db.js`:
- Around line 78-92: The inner try/catch around the session_names migration (the
db.exec calls that create the session_names table and idx_session_names_lookup)
is swallowing real DB errors; remove that inner try/catch so exceptions from
db.exec bubble up to the outer runMigrations handler (or, if you want to log,
call console.warn and then re-throw the error) so genuine failures (disk full,
permission denied, corruption) are not suppressed.
---
Nitpick comments:
In `@server/database/db.js`:
- Around line 379-392: getNames currently builds a different SQL string for
every batch size and spreads sessionIds into .all(), which busts the prepare
cache and can exceed SQLITE_MAX_VARIABLE_NUMBER; change getNames to process
sessionIds in fixed-size chunks (e.g., 500) and query with a stable prepared
statement that accepts a single JSON array parameter (e.g., "WHERE session_id IN
(SELECT value FROM json_each(?)) AND provider = ?"), calling
.all(JSON.stringify(chunk), provider) for each chunk and merging results; update
any callers like applyCustomSessionNames to rely on the new getNames behavior so
large batches are handled without cache misses or parameter-count errors.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
server/index.js (1)
1632-1642:⚠️ Potential issue | 🟡 MinorInconsistent
safeSessionIdvalidation in the token-usage endpoint — missing mismatch check.The rename endpoint (lines 544–547) correctly rejects when the sanitized
sessionIddiffers from the original. The token-usage endpoint at line 1639 still uses the old pattern — it only guards against an empty result, silently accepting a mutated ID if the original contained characters outside[a-zA-Z0-9._-].🐛 Proposed fix to align with the rename endpoint
const safeSessionId = String(sessionId).replace(/[^a-zA-Z0-9._-]/g, ''); - if (!safeSessionId) { + if (!safeSessionId || safeSessionId !== String(sessionId)) { return res.status(400).json({ error: 'Invalid sessionId' }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/index.js` around lines 1632 - 1642, The token-usage route handler app.get('/api/projects/:projectName/sessions/:sessionId/token-usage', authenticateToken, ...) currently only checks that safeSessionId is non-empty after sanitizing; change the validation to also reject when the sanitized ID differs from the original (i.e., if safeSessionId !== String(sessionId)) and return 400 with an "Invalid sessionId" error, matching the rename endpoint behavior so mutated IDs are not silently accepted.
🧹 Nitpick comments (1)
server/index.js (1)
541-565: Rename endpoint validation is well-structured; minor: hoistVALID_PROVIDERSto module scope.The
safeSessionIdmismatch-rejection (lines 544–547) correctly implements the fix from the previous review. Thesummaryandprovidervalidations are appropriately guarded. The synchronoussessionNamesDb.setNamecall is correct givenbetter-sqlite3's synchronous API.
VALID_PROVIDERSis an array literal defined inside the handler and re-allocated on every request. Moving it to module scope is a zero-risk improvement.♻️ Proposed refactor
+const VALID_PROVIDERS = ['claude', 'codex', 'cursor']; + // Rename session endpoint app.put('/api/sessions/:sessionId/rename', authenticateToken, async (req, res) => { try { ... - const VALID_PROVIDERS = ['claude', 'codex', 'cursor']; if (!provider || !VALID_PROVIDERS.includes(provider)) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/index.js` around lines 541 - 565, Hoist the VALID_PROVIDERS array out of the request handler to module scope to avoid reallocating it per request: define const VALID_PROVIDERS = ['claude','codex','cursor'] near the top of the file and remove the in-handler declaration; keep the handler app.put('/api/sessions/:sessionId/rename', authenticateToken, async (req, res) => { ... }) unchanged except to reference the module-scoped VALID_PROVIDERS when validating provider before calling sessionNamesDb.setName(safeSessionId, provider, summary.trim()).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@server/index.js`:
- Around line 1632-1642: The token-usage route handler
app.get('/api/projects/:projectName/sessions/:sessionId/token-usage',
authenticateToken, ...) currently only checks that safeSessionId is non-empty
after sanitizing; change the validation to also reject when the sanitized ID
differs from the original (i.e., if safeSessionId !== String(sessionId)) and
return 400 with an "Invalid sessionId" error, matching the rename endpoint
behavior so mutated IDs are not silently accepted.
---
Nitpick comments:
In `@server/index.js`:
- Around line 541-565: Hoist the VALID_PROVIDERS array out of the request
handler to module scope to avoid reallocating it per request: define const
VALID_PROVIDERS = ['claude','codex','cursor'] near the top of the file and
remove the in-handler declaration; keep the handler
app.put('/api/sessions/:sessionId/rename', authenticateToken, async (req, res)
=> { ... }) unchanged except to reference the module-scoped VALID_PROVIDERS when
validating provider before calling sessionNamesDb.setName(safeSessionId,
provider, summary.trim()).
|
@PaloSP I don't think this works with Codex and Cursor sessions. Can you update it? |
fixes siteboon#358) - Add session_names table to store custom display names per provider - Add PUT /api/sessions/:sessionId/rename endpoint - Replace stub updateSessionSummary with real API call - Apply custom names across all providers (Claude, Codex, Cursor) - Fix project rename destroying config (spread merge instead of overwrite) - Thread provider parameter through sidebar component chain - Add i18n error messages for rename failures (en, ja, ko, zh-CN)
- Log migration errors instead of swallowing them silently (db.js) - Add try/catch to applyCustomSessionNames to prevent getProjects abort - Move applyCustomSessionNames to db.js as shared helper (DRY) - Fix Cursor getSessionName to check session.summary for custom names - Move edit state clearing to finally block in updateSessionSummary - Sanitize sessionId, add 500-char summary limit, validate provider whitelist - Remove dead applyCustomSessionNames call on empty manual project sessions
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…phans - Enable rename UI (pencil icon) for Codex, Cursor, and Gemini sessions - Keep delete button hidden for Cursor (no backend delete endpoint) - Add 'gemini' to VALID_PROVIDERS and hoist to module scope - Add sessionNamesDb.deleteName on session delete (claude, codex, gemini) - Fix token-usage endpoint sessionId mismatch validation - Remove redundant try/catch in sessionNamesDb methods - Let session_names migration errors propagate to outer handler
3cdd067 to
75198e8
Compare
|
@blackmammoth Fixed — rename now works for all providers (Claude, Codex, Cursor, Gemini). The issue was that the edit/delete hover buttons in Tested with Claude and Codex. Don't have Cursor/Gemini set up to test locally. |
blackmammoth
left a comment
There was a problem hiding this comment.
Tested and works well.
|
@PaloSP I see. It's all working now. I have also tested it with Gemini and it works. Merged! |
* feat: implement session rename with SQLite storage (closes siteboon#72, fixes siteboon#358) - Add session_names table to store custom display names per provider - Add PUT /api/sessions/:sessionId/rename endpoint - Replace stub updateSessionSummary with real API call - Apply custom names across all providers (Claude, Codex, Cursor) - Fix project rename destroying config (spread merge instead of overwrite) - Thread provider parameter through sidebar component chain - Add i18n error messages for rename failures (en, ja, ko, zh-CN) * fix: address CodeRabbit review feedback for session rename - Log migration errors instead of swallowing them silently (db.js) - Add try/catch to applyCustomSessionNames to prevent getProjects abort - Move applyCustomSessionNames to db.js as shared helper (DRY) - Fix Cursor getSessionName to check session.summary for custom names - Move edit state clearing to finally block in updateSessionSummary - Sanitize sessionId, add 500-char summary limit, validate provider whitelist - Remove dead applyCustomSessionNames call on empty manual project sessions * fix: reject sessionId on mismatch instead of silent normalization Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix: enable rename for all providers, add Gemini support, clean up orphans - Enable rename UI (pencil icon) for Codex, Cursor, and Gemini sessions - Keep delete button hidden for Cursor (no backend delete endpoint) - Add 'gemini' to VALID_PROVIDERS and hoist to module scope - Add sessionNamesDb.deleteName on session delete (claude, codex, gemini) - Fix token-usage endpoint sessionId mismatch validation - Remove redundant try/catch in sessionNamesDb methods - Let session_names migration errors propagate to outer handler --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Haileyesus <118998054+blackmammoth@users.noreply.github.com>
Summary
session_namestableChanges
session_namestable,PUT /api/sessions/:sessionId/renameendpoint,sessionNamesDbhelper with UPSERT, migrationupdateSessionSummarywith real API call, threadedproviderparam through sidebar component chainrenameProject()now uses spread merge{ ...config[projectName], displayName }instead of overwriting entire configWhy SQLite instead of .jsonl?
Claude CLI owns
.jsonlsession files and can overwrite custom names via auto-titling after/resume(see anthropics/claude-code#25090). Storing names in the existing SQLite DB avoids this entirely.Test plan
manuallyAddedandoriginalPathare preservedSummary by CodeRabbit
New Features
Internationalization