fix(gateway): isolate multiplex profile sessions and voice state - #75198
davidxyuan wants to merge 4 commits into
Conversation
Preserve current upstream changes while applying only the six profile/session/voice isolation files.
|
Thanks for the focused multiplex isolation work. The session fallback premise is real on current main: Problems
Suggested changes
Automated hermes-sweeper review. |
…0731 # Conflicts: # apps/desktop/src/app/chat/composer/rich-editor.ts
|
Addressed the sweeper feedback and updated the branch onto current
Local CI-parity tests:
The latest GitHub Actions run is still |
|
Thanks for this PR. Merged via #101248 (ad8b9e0) on current main — per-profile /voice state, off-loop startup hydration, session-recovery owner fence, int route ids. #101248 won as the consolidated fix because it covers the whole multiplex-profile bug class in one change (with tests) rather than the single symptom addressed here; this PR is superseded by it. If anything from your original change is still missing on main >= ad8b9e0, please open a fresh PR/issue against main and tag it. Thanks again. |
…file durable state The per-profile store model (#88734), the parent-inheritance fence (#88381), profile-stamped topic rows (#76423) and profile-prefixed voice keys (#75198) are all forward-only: they put NEW state under the right profile and refuse to widen existing damage, but nothing walks the stores and settles what earlier releases left crossed. #113884 found 246 sessions stranded that way and could only warn. `hermes sessions repair-profiles` scans every profile's state.db plus the gateway's voice-mode and sessions.json files and names six kinds of crossing: 1. `profile_name` disagreeing with the row's own session key -> relabel; 2. rows physically in another profile's store -> move (all message generations, usage rows, system prompt) to the owning store, parents before children so lineage survives, copy-then-delete so a crash leaves a duplicate the next run settles; 3. `parent_session_id` crossing namespaces -> sever (own identity kept); 4. routing rows outside the default store under multiplexing -> move (an existing row wins); routing rows for a profile that no longer exists -> drop; 5. Telegram topic bindings and voice-mode entries missing their bot's profile -> relabel from the sessions that hold the chat (ambiguous chats reported); 6. sessions.json mirror entries for an unclaimed namespace -> drop (the legacy import re-injects them into routing every boot). Report-only by default. `--apply` refuses while a gateway owns any store, takes a quick snapshot of every store first, and is idempotent. Two cases are reported but never guessed: rows keyed to a profile that does not exist, and `agent:main` rows inside a named profile's store (`--legacy-main rekey|move` says which of the two histories they are). Storage side lives in `hermes_state_profile_repair.py` (SessionDB mixin); orchestration across stores in `hermes_cli/sessions_repair_profiles.py`; the CLI face in `hermes_cli/sessions_cmd_repair_profiles.py` (pre-DB handler: it opens every store itself). Part of #88715 (PR-6). Closes the remediation gap #113884 only warns about.
Summary
Isolate gateway session recovery and persisted voice mode by multiplex profile.
This is not the Auto-TTS OGG/Opus issue from #73486. Current main already contains the platform-aware Auto-TTS output-path fix from
1753369f7c3c4797e26ec446d5892619149c9728; this PR does not modifybuild_auto_tts_output_path, Telegram.ogghandling, orOPUS_VOICE_PLATFORMS.Problem
In multiplex mode, the default profile and a named profile such as
catgirlcan serve the same Telegram user/chat tuple.Two current-main behaviors are not profile-scoped:
find_latest_gateway_session_for_peer()falls back by platform/user/chat/thread without constrainingprofile_name._voice_key()persists state as onlyplatform:chat_id.If the routing index is temporarily missing, a named profile can recover the default profile's session. Voice state and adapter synchronization can also cross profiles that share the same platform/chat tuple.
Changes
profile_nametofind_latest_gateway_session_for_peer().telegram:chat_id; useprofile:telegram:chat_idfor named profiles./voice, adapter synchronization, startup/reconnect, voice join/leave/timeout, and voice replies through the source profile's adapter/state.Reproduction
defaultandcatgirlusing the same Telegram user/chat tuple.default./voiceforcatgirl.Before this change, the peer fallback can select the default session and the persisted voice key is shared. After this change, the named profile sees only its own session and voice state.
Files
gateway/run.pygateway/session.pygateway/slash_commands.pyhermes_state.pytests/gateway/test_voice_mode_platform_isolation.pytests/test_hermes_state.pyValidation
The original combined change set previously completed 295 tests with 0 failures. This PR was split to the six profile-isolation files and three-way merged onto current upstream main (
b1858f33a1cc6083ee34aea41417ea5e18564d2e) with no conflicts. Targeted current-main CI is pending on this PR.Intended focused command: