From 181641db6b203ce4fbd5cb2f511a86c0483c5d93 Mon Sep 17 00:00:00 2001 From: Nathan Esquenazi Date: Sat, 4 Apr 2026 17:33:55 -0700 Subject: [PATCH] fix: allow deleting CLI sessions from sidebar (#87) The delete endpoint only removed sessions from the WebUI JSON store, silently no-oping on CLI sessions (which live in state.db). The trash button showed 'Conversation deleted' but the session reappeared on next refresh. Fix: after the existing WebUI delete, also call delete_cli_session() which removes the session + messages from state.db. Wrapped in try/except so WebUI-only sessions still delete normally. New delete_cli_session() in api/models.py mirrors the existing get_cli_session_messages() pattern for state.db access. Closes #87 Co-authored-by: Claude Opus 4.6 (1M context) --- api/models.py | 30 ++++++++++++++++++++++++++++++ api/routes.py | 6 ++++++ 2 files changed, 36 insertions(+) diff --git a/api/models.py b/api/models.py index 459a37e..0839fdf 100644 --- a/api/models.py +++ b/api/models.py @@ -336,3 +336,33 @@ def get_cli_session_messages(sid): except Exception: return [] return msgs + + +def delete_cli_session(sid): + """Delete a CLI session from state.db (messages + session row). + Returns True if deleted, False if not found or error. + """ + import os + try: + import sqlite3 + except ImportError: + return False + + try: + from api.profiles import get_active_hermes_home + hermes_home = Path(get_active_hermes_home()).expanduser().resolve() + except Exception: + hermes_home = Path(os.getenv('HERMES_HOME', str(HOME / '.hermes'))).expanduser().resolve() + db_path = hermes_home / 'state.db' + if not db_path.exists(): + return False + + try: + with sqlite3.connect(str(db_path)) as conn: + cur = conn.cursor() + cur.execute("DELETE FROM messages WHERE session_id = ?", (sid,)) + cur.execute("DELETE FROM sessions WHERE id = ?", (sid,)) + conn.commit() + return cur.rowcount > 0 + except Exception: + return False diff --git a/api/routes.py b/api/routes.py index 87737f8..67ad0e9 100644 --- a/api/routes.py +++ b/api/routes.py @@ -358,12 +358,18 @@ def handle_post(handler, parsed): if parsed.path == '/api/session/delete': sid = body.get('session_id', '') if not sid: return bad(handler, 'session_id is required') + # Delete from WebUI session store with LOCK: SESSIONS.pop(sid, None) p = SESSION_DIR / f'{sid}.json' try: p.unlink(missing_ok=True) except Exception: pass try: SESSION_INDEX_FILE.unlink(missing_ok=True) except Exception: pass + # Also delete from CLI state.db (for CLI sessions shown in sidebar) + try: + from api.models import delete_cli_session + delete_cli_session(sid) + except Exception: pass return j(handler, {'ok': True}) if parsed.path == '/api/session/clear':