Skip to content

[Refactor]: Unify channel account-list helpers with binding-aware default resolution #30967

@mcaxtr

Description

@mcaxtr

Summary

Enhance the shared createAccountListHelpers() to support binding-aware default account resolution, account ID normalization, and scoped-binding filtering — eliminating per-channel reimplementations and fixing 5+ multi-account bugs.

Problem to Solve

The shared createAccountListHelpers() in src/channels/plugins/account-helpers.ts had three gaps:

  1. No binding-aware default resolutionresolveDefaultAccountId() ignored agent bindings entirely. Only Telegram had custom code for this, so Discord, Slack, Signal, iMessage, and WhatsApp silently failed in multi-account + binding setups: bindings were configured but the default account was always "default" or the first alphabetical account.

  2. No account ID normalizationlistConfiguredAccountIds() didn't normalize keys, so mixed-case config entries (e.g., "Work" vs "work") could cause duplicate accounts or lookup mismatches.

  3. Scoped bindings leaked into global defaultresolveDefaultAgentBoundAccountId() returned the first matching binding regardless of scope constraints (guildId, teamId, peer, roles). A guild-specific Discord binding could become the global default account for CLI, onboarding, and status paths.

Additionally, Telegram's custom approach blindly merged listBoundAccountIds() into the account list — creating phantom accounts for bindings that referenced non-existent accounts.

Proposed Solution

  • Enhanced shared helper with lowercase-normalized IDs, binding-aware resolveDefaultAccountId() (validated against configured accounts), and validateBoundAccountIds() diagnostic helper
  • resolveDefaultAgentBoundAccountId() now skips scoped bindings (peer/guildId/teamId/roles)
  • Migrated Telegram to use the shared helper with channel-specific hasBaseLevelTelegramToken() for implicit default account detection (including tokenFile)
  • Added binding-aware default resolution to LINE's resolveDefaultLineAccountId()
  • 25 unit tests covering all behaviors

Alternatives Considered

  • Adding binding support individually per channel — perpetuates the duplication that caused this drift
  • Blindly merging bound IDs into account list (Telegram's old approach) — creates phantom accounts per user feedback

Impact

  • Affected: Discord, Slack, Signal, iMessage, WhatsApp, Telegram, LINE account resolution + resolveDefaultAgentBoundAccountId() in routing/bindings
  • Severity: Bug (silent multi-account failures)
  • Frequency: Every multi-account setup with bindings
  • Consequence: Ack reactions fail, cron announcements fail, wrong account picked for delivery

Related Issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions