fix: sync generated title to state.db so hermes sessions list shows WebUI titles

Background title generation (`_run_background_title_update` and
`_run_background_title_refresh`) writes the title to the WebUI sidecar
JSON but never syncs it back to hermes-agent's `state.db`. This causes
`hermes sessions list` to show blank titles for all WebUI-originated
sessions.

The existing `sync_session_usage` path is gated by the `sync_to_insights`
setting (default: off) and executes before the background title thread
completes, so it cannot cover this case.

Fix: add `sync_session_title()` to `state_sync.py` — an unconditional
(not gated by `sync_to_insights`) title-only sync — and call it from both
title generation paths immediately after persisting the title to the
sidecar.
This commit is contained in:
liuguangyong93 committed 2026-08-12 14:48:10 +08:00
1 parent e9f1e2a459
commit ae8311915b
2 files changed
+38

No files matched your search

+26
View File
@@ -195,3 +195,29 @@ def sync_session_usage(session_id: str, input_tokens: int=0, output_tokens: int=
db.close()
except Exception:
logger.debug("Failed to close state.db")
def sync_session_title(session_id: str, title: str, profile: Optional[str] = None) -> None:
"""Sync a generated title to state.db unconditionally (not gated by sync_to_insights).
Background title generation writes the title to the WebUI sidecar JSON but
not to hermes-agent's state.db, so ``hermes sessions list`` shows blank
titles for WebUI sessions. This function bridges that gap and is called
from the background title update/refresh paths after a title is persisted.
"""
if not title:
return
db = _get_state_db(profile=profile)
if not db:
return
try:
# Ensure the session row exists (idempotent) so the UPDATE has a target.
db.ensure_session(session_id=session_id, source='webui')
db.set_session_title(session_id, title)
except Exception:
logger.debug("Failed to sync session title to state.db for %s", session_id)
finally:
try:
db.close()
except Exception:
logger.debug("Failed to close state.db")
+12
View File
@@ -4466,6 +4466,12 @@ def _run_background_title_update(session_id: str, user_text: str, assistant_text
else:
_put_title_status(put_event, session_id, source, llm_status, effective_title, raw_preview)
put_event('title', {'session_id': session_id, 'title': effective_title})
# Sync the generated title to state.db so `hermes sessions list` shows it.
try:
from api.state_sync import sync_session_title
sync_session_title(session_id, effective_title, profile=getattr(s, 'profile', None))
except Exception:
logger.debug("Failed to sync title to state.db after generation for %s", session_id)
else:
_put_title_status(put_event, session_id, 'skipped', source or 'unchanged', effective_title, raw_preview)
finally:
@@ -4538,6 +4544,12 @@ def _run_background_title_refresh(session_id: str, user_text: str, assistant_tex
s.save(touch_updated_at=False)
_put_title_status(put_event, session_id, 'refreshed', llm_status, effective_title, raw_preview)
put_event('title', {'session_id': session_id, 'title': effective_title})
# Sync the refreshed title to state.db so `hermes sessions list` stays current.
try:
from api.state_sync import sync_session_title
sync_session_title(session_id, effective_title, profile=getattr(s, 'profile', None))
except Exception:
logger.debug("Failed to sync refreshed title to state.db for %s", session_id)
logger.info("Adaptive title refresh: session=%s new_title=%r", session_id, effective_title)
except Exception:
logger.debug("Background title refresh failed for session %s", session_id, exc_info=True)