Skip to content

fix(gateway): namespace telegram topic mode by profile under multiplex - #76487

Open
crdesign8 wants to merge 4 commits into
NousResearch:mainfrom
crdesign8:fix/telegram-topic-profile-dimension
Open

fix(gateway): namespace telegram topic mode by profile under multiplex#76487
crdesign8 wants to merge 4 commits into
NousResearch:mainfrom
crdesign8:fix/telegram-topic-profile-dimension

Conversation

@crdesign8

Copy link
Copy Markdown

What does this PR do?

When multiplex_profiles: true, every profile shares one state.db. The Telegram topic-mode tables were keyed only by Telegram identifiers:

  • telegram_dm_topic_mode — PK (chat_id)
  • telegram_dm_topic_bindings — PK (chat_id, thread_id)

In private chats, chat_id is the user's Telegram id — identical across bots. Multiple profiles therefore collide on the same rows: last write wins, thread bindings get overwritten, replies fail with "Message thread not found."

This PR adds a profile_name dimension to both tables and threads the routed profile (source.profile) through every gateway access path. Legacy rows migrate into the "default" namespace only (no replication across configured profiles).

Split into two commits for review:

  1. fix(state) — schema v3 + SessionDB API (profile_name="default" keyword-only for compat)
  2. fix(gateway) — wire source.profile into call sites; stamp adapters for prune under multiplex

Related Issue

Fixes #76423

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

Commit 1 — state layer

  • hermes_state.py
    • profile_name TEXT NOT NULL DEFAULT 'default' on both topic tables
    • PKs: (profile_name, chat_id) and (profile_name, chat_id, thread_id)
    • Schema gate telegram_dm_topic_schema_versionv3 (table rebuild; legacy → default)
    • Keyword-only profile_name="default" on enable/disable/is/get/list/bind/delete/list_unlinked
  • tests/gateway/test_telegram_topic_profile_isolation_76423.py (new) — migration + cross-profile isolation + default-kwarg compat
  • tests/test_hermes_state.py — expect schema version "3"

Commit 2 — gateway wiring

  • gateway/run.py_telegram_topic_profile_name(source) from source.profile (never process-global active profile); pass through mode/bind/recover/disable/restore/rename; stamp adapter._hermes_profile_name on primary + secondary adapters
  • gateway/slash_commands.py/topic enable + binding status
  • plugins/platforms/telegram/adapter.py — prune uses adapter profile stamp
  • tests/gateway/test_telegram_topic_profile_routing_76423.py (new) — routed profile isolation at gateway layer

How to Test

  1. Checkout the branch

  2. Activate the project venv

  3. Focused suite (CI-parity wrapper):

    scripts/run_tests.sh \
      tests/gateway/test_telegram_topic_profile_isolation_76423.py \
      tests/gateway/test_telegram_topic_profile_routing_76423.py \
      tests/gateway/test_telegram_prune_stale_topic_binding_31501.py \
      tests/gateway/test_telegram_topic_mode.py \
      -q

    Expected: 31 passed.

  4. Optional manual (multiplex fleet):

    • Enable multiplex_profiles: true with two Telegram-using profiles
    • /topic on bot A, open a topic, chat
    • Same user on bot B → topic mode / bindings for A must not be clobbered

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass — full suite is ~70+ minutes locally; not run end-to-end here. Focused suite for this change passes (see How to Test). CI will run the full matrix on the PR.
  • I've added tests for my changes
  • I've tested on my platform: Linux

Documentation & Housekeeping

  • N/A — I've updated relevant documentation (internal SessionDB/gateway state; behavior covered by tests + method docs)
  • N/A — I've updated cli-config.yaml.example (no new config keys)
  • N/A — I've updated CONTRIBUTING.md or AGENTS.md
  • N/A — I've considered cross-platform impact (SQLite + pure Python; no OS-specific I/O)
  • N/A — I've updated tool descriptions/schemas

Notes

Focused verification:

scripts/run_tests.sh \
  tests/gateway/test_telegram_topic_profile_isolation_76423.py \
  tests/gateway/test_telegram_topic_profile_routing_76423.py \
  tests/gateway/test_telegram_prune_stale_topic_binding_31501.py \
  tests/gateway/test_telegram_topic_mode.py -q
→ 31 passed

Issue NousResearch#76423: under multiplex_profiles a shared state.db keyed topic mode
and bindings only by Telegram chat_id/thread_id, so private-chat ids
collided across bots/profiles.

- Add profile_name to telegram_dm_topic_mode and telegram_dm_topic_bindings
- Schema v2→v3 rebuild; legacy rows migrate into the "default" namespace
- Keyword-only profile_name="default" on SessionDB topic APIs (compat)
Issue NousResearch#76423 follow-up: wire SessionDB profile_name through gateway paths.

- Resolve profile from source.profile (never process-global active profile)
- Stamp adapter._hermes_profile_name for prune under multiplex
- /topic enable/status and binding record/recover/disable/restore paths
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/telegram Telegram bot adapter area/config Config system, migrations, profiles area/profiles Multi-profile isolation, HERMES_HOME scoping sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 2, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracing the shared-state.db collision and covering the schema migration plus routed SessionDB calls. The core premise is confirmed on current main: hermes_state.py:7965-7988 keys the topic tables only by Telegram IDs.

Problems

  • plugins/platforms/telegram/adapter.py:1323 derives the prune namespace from a static adapter stamp. That is unsafe for profile_routes: gateway/platforms/base.py:6638-6641 retains the receiving transport adapter even when source.profile selects a different runtime, and gateway/authz_mixin.py:101-107 returns that transport for delivery. A primary adapter can therefore prune the default namespace for a routed non-default turn.
  • Runner-level topic cooldowns are still keyed only by chat_id (gateway/run.py:6431-6439, 19187-19195), so profiles sharing a Telegram DM ID suppress each other's reminder/setup-hint messages.
  • website/docs/user-guide/messaging/telegram.md:830-852 documents the old table keys and unscoped manual cleanup SQL.

Suggested changes

  • Carry the routed profile through outbound Telegram metadata and use it for stale-binding pruning; add a primary-adapter/profile-route regression test.
  • Namespace the two cooldown maps and /topic off cleanup by (profile, chat_id).
  • Update the Telegram persistence and cleanup documentation.

Automated hermes-sweeper review.

Comment thread plugins/platforms/telegram/adapter.py Outdated
@teknium1 teknium1 added the sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform label Aug 2, 2026
Address hermes-sweeper review on NousResearch#76487:

- Prefer hermes_profile from send metadata when pruning stale topic
  bindings so profile_routes cannot delete the transport adapter's
  namespace instead of the routed runtime's
- Namespace lobby/capability cooldowns and /topic off cleanup by
  (profile, chat_id)
- Document profile_name PKs and scoped cleanup SQL in telegram.md
- Regression: primary-adapter stamp + routed metadata prune isolation
@crdesign8

Copy link
Copy Markdown
Author

Addressed the sweeper review points:

  1. Prune namespace_prune_stale_dm_topic_binding now prefers metadata["hermes_profile"] (routed) over the adapter stamp. Outbound thread metadata stamps hermes_profile from source.profile. Regression test: primary adapter stamped default prunes only the coder binding when metadata says so.
  2. Cooldowns — lobby reminder + capability hint maps keyed by (profile, chat_id); /topic off clears that key only.
  3. Docswebsite/docs/user-guide/messaging/telegram.md updated for profile_name PKs and scoped cleanup SQL.

New commit: fix(gateway): route profile into topic prune, cooldowns, and docs
Focused suite: 34 passed.

@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

One PR addresses #76423. #76487 fixes the reported shared-state collision by adding a profile dimension to Telegram topic-mode state and propagating the routed profile through database, gateway, pruning, cooldown, test, and documentation paths.

Related pull requests

Suggested consolidation

Keep #76487 open with a salvage path: retain the profile-scoped schema migration, routed-profile SessionDB calls, metadata-based prune namespace, profile-scoped cooldowns, documentation updates, and regression coverage, then obtain contributor re-review of the changes made in response to the keep_open review. There are no duplicate PRs to close.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I76423(["issue #76423 (open)"])
    P76487["PR #76487 (open)"]
    P76487 -->|best fix| I76423
    class I76423 open
    class P76487 open
    class P76487 best
    class P76487 target
    click I76423 "https://github.com/NousResearch/hermes-agent/issues/76423"
    click P76487 "https://github.com/NousResearch/hermes-agent/pull/76487"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 1 pull request and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 58 kB of PR diffs, 8 kB of issue/PR text, 5 kB of discussion (4 comments), 2 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

…profile-dimension

# Conflicts:
#	hermes_state.py
@crdesign8

Copy link
Copy Markdown
Author

This is the second time since I made this PR that it has major conflicts. I solved first one, and all the requirements needed, but this is awaiting for review and workflow approval for couple days. So, I'll not fix those conflicts anymore, because it's waste of time, no one is looking for it or another PRs that was made for non contributer, even if it's a bug solver. Thanks anyway, @teknium1 and @GottZ .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles area/profiles Multi-profile isolation, HERMES_HOME scoping comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/telegram Telegram bot adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multiplex: telegram_dm_topic_mode and telegram_dm_topic_bindings lack profile dimension in shared state.db

4 participants