refactor(sessions): one session-id minter; QQ update-prompt key from build_session_key

Nine f-string sites minted `YYYYMMDD_HHMMSS_<hex>` independently with the hex width already
drifted (6 on CLI/TUI/agent/import, 8 in the gateway store, 12 in portability imports).
hermes_cli/session_lost_and_found.py classifies schema-less salvage rows by that shape, so a
site drifting the prefix would silently change recovery. hermes_state_ids.new_session_id(now,
hex_len=) is now the only writer and owns SESSION_ID_PATTERN; stdlib-only so agent/, cli.py and
gateway/ can import it without the SessionDB graph.

Widths are kept per site on purpose: the Desktop's session-id candidate regex is pinned to 6 hex
chars for interactive ids; the gateway store and portability importer keep 8/12 (more rows per
second). Not a bug, so not "fixed".

gateway/platforms/qqbot/adapter.py hard-coded `agent:main:qqbot:<scene>:<chat>` for the
update-prompt authz key, ignoring the profile namespace build_session_key applies; a secondary
bot in a multiplexed gateway got `agent:<profile>:...` keys and its clicks were rejected. The key
now comes from the one builder via BasePlatformAdapter._source_session_key.

Behavior change: QQ update-prompt clicks are authorized under the profile-namespaced key
(byte-identical `agent:main:` for the default profile).
This commit is contained in:
teknium1
2026-09-12 22:04:19 -07:00
committed by Teknium
parent c1e58f4cb2
commit 991b23ad8d
13 changed files with 35 additions and 24 deletions
+2 -2
View File
@@ -12,6 +12,7 @@ from typing import Any, Dict, List, Optional
from agent.skill_commands import SKILL_SCAFFOLD_SQL_LIKE
from utils import safe_json_loads
from hermes_cli.timefmt import coerce_epoch
from hermes_state_ids import new_session_id
from hermes_state_common import SCHEMA_SQL, _PREVIEW_RAW_SUBQUERY_SQL, _shape_preview, _sql_session_last_active
# Pre-split logger identity so log filtering/capture is unchanged.
@@ -106,8 +107,7 @@ class SessionPortabilityMixin:
Reuse the portability validator and message writer so counters and FTS
obey the same contract as ordinary transcript imports.
"""
import uuid
session_id = f"{time.strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:12]}"
session_id = new_session_id(hex_len=12)
normalized, errors = self._validate_import_payload([
{"id": session_id, "source": origin["tool"], "title": title,
"cwd": cwd, "messages": messages}])