Skip to content

fix: multi-bot Telegram routing uses account_id, not first-match on allowed_users#2183

Merged
houko merged 2 commits into
mainfrom
fix/multi-bot-telegram-routing
Apr 8, 2026
Merged

fix: multi-bot Telegram routing uses account_id, not first-match on allowed_users#2183
houko merged 2 commits into
mainfrom
fix/multi-bot-telegram-routing

Conversation

@houko

@houko houko commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • resolve_with_context looked up channel_defaults with the plain key "Telegram", but multi-bot entries are stored under "Telegram:<account_id>" — so account-specific defaults never matched
  • A shared user (present in both bots' allowed_users) always hit the first-registered bot regardless of which token received the message
  • Fix: probe the account-qualified key "Telegram:<account_id>" before falling back to the generic key

Test plan

  • New regression test test_multi_bot_account_id_routing covers the exact scenario from the issue
  • cargo test -p librefang-channels router all pass

Fixes #2140

houko and others added 2 commits April 8, 2026 15:07
…llowed_users

resolve_with_context only looked up the generic "Telegram" key in
channel_defaults, but keys are stored as "Telegram:<account_id>" when
multiple bots are configured. A user present in both bots' allowed_users
always matched the first-registered bot regardless of which token
received the message.

Fix: before falling back to the generic channel key, probe the
account-specific key "Telegram:<account_id>" when ctx.account_id is
present. Adds a regression test covering the exact scenario from #2140.

Fixes #2140
@github-actions github-actions Bot added area/channels Messaging channel adapters area/runtime Agent loop, LLM drivers, WASM sandbox area/kernel Core kernel (scheduling, RBAC, workflows) labels Apr 8, 2026
@houko
houko enabled auto-merge (squash) April 8, 2026 06:22
@houko
houko merged commit 6b160b4 into main Apr 8, 2026
10 of 14 checks passed
@houko
houko deleted the fix/multi-bot-telegram-routing branch April 8, 2026 06:23
houko added a commit that referenced this pull request May 24, 2026
…lation)

Channel-side slash commands (/btw, /new, /reboot, /compact, /model, /stop,
/usage, /think, plus the model: interactive-button callback) called the
context-less AgentRouter::resolve, which has no account_id parameter and
only probes the plain channel key. With per-bot defaults stored under
qualified "<channel>:<account_id>" keys (since #2183 / #4861), the lookup
fell through to the system default — collapsing every bot's /command to
the first-registered agent.

/agent <name> stored its override via set_user_default keyed by
platform_id only, with no channel scope, so a /agent in bot-a silently
overrode every other bot's default_agent for that user until daemon
restart (user_defaults is in-memory DashMap, never persisted).

Fix re-keys user_defaults as (Option<channel_account_key>, user_key);
resolve_with_context probes the per-(channel,account) slot first and falls
back to the legacy global slot. handle_command gains an account_id
parameter, builds a BindingContext per call via a resolve_for_command
closure, and writes /agent's selection via the new
set_user_default_for_channel helper. The model: button callback and
resolve_or_fallback's auto-bind path are migrated identically — otherwise
the fallback path would silently re-introduce the leak.

5 regression tests pin the fix:
- router::user_default_does_not_leak_across_bots
- router::channel_scoped_user_default_overrides_global
- router::legacy_set_user_default_is_channel_agnostic
- bridge::command_resolution_respects_account_id (records set_model
  dispatch agent_ids; /model from bot-b then bot-c records
  [agent_b, agent_c] — pre-fix this would be [agent_a, agent_a])
- bridge::agent_command_does_not_leak_across_bots

cargo test -p librefang-channels --lib → 465/465 green.
cargo clippy -p librefang-channels --all-targets -- -D warnings clean.

Closes #5672
houko added a commit that referenced this pull request May 24, 2026
…lation) (#5688)

Channel-side slash commands (/btw, /new, /reboot, /compact, /model, /stop,
/usage, /think, plus the model: interactive-button callback) called the
context-less AgentRouter::resolve, which has no account_id parameter and
only probes the plain channel key. With per-bot defaults stored under
qualified "<channel>:<account_id>" keys (since #2183 / #4861), the lookup
fell through to the system default — collapsing every bot's /command to
the first-registered agent.

/agent <name> stored its override via set_user_default keyed by
platform_id only, with no channel scope, so a /agent in bot-a silently
overrode every other bot's default_agent for that user until daemon
restart (user_defaults is in-memory DashMap, never persisted).

Fix re-keys user_defaults as (Option<channel_account_key>, user_key);
resolve_with_context probes the per-(channel,account) slot first and falls
back to the legacy global slot. handle_command gains an account_id
parameter, builds a BindingContext per call via a resolve_for_command
closure, and writes /agent's selection via the new
set_user_default_for_channel helper. The model: button callback and
resolve_or_fallback's auto-bind path are migrated identically — otherwise
the fallback path would silently re-introduce the leak.

5 regression tests pin the fix:
- router::user_default_does_not_leak_across_bots
- router::channel_scoped_user_default_overrides_global
- router::legacy_set_user_default_is_channel_agnostic
- bridge::command_resolution_respects_account_id (records set_model
  dispatch agent_ids; /model from bot-b then bot-c records
  [agent_b, agent_c] — pre-fix this would be [agent_a, agent_a])
- bridge::agent_command_does_not_leak_across_bots

cargo test -p librefang-channels --lib → 465/465 green.
cargo clippy -p librefang-channels --all-targets -- -D warnings clean.

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

Labels

area/channels Messaging channel adapters area/kernel Core kernel (scheduling, RBAC, workflows) area/runtime Agent loop, LLM drivers, WASM sandbox

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Multi-bot Telegram routing uses allowed_users first-match instead of bot token

1 participant