Skip to content

Router default-agent state is keyed inconsistently — defaults bleed across channels and collide between users #1120

@benhoverter

Description

@benhoverter

Description

The Discord and Slack channel adapters store and look up "default agent per channel" using keys that don't include the invoking user. As a result, /agent <name> writes overwrite each other across users on a shared channel, and reads pick up whichever value was written most recently — regardless of who's asking. Two distinct user-visible symptoms:

  1. Cross-user collisions on shared channels. When two users share a channel (e.g. #general), the second user's /agent <name> overwrites the first user's default for that channel — silently. The first user's next mention routes to the wrong agent.
  2. Defaults bleed across channels. A user setting /agent assistant in #general ends up affecting agent resolution in other channels for the same platform installation.

Context: every message that targets the bot is @mention-prefixed (there's only one bot per OpenFang daemon). The bug isn't about implicit routing — the mention is always present. The bug is about which underlying agent the bot dispatches to once mentioned.

Root cause (for triage)

In openfang/crates/openfang-channels/src/bridge.rs, the router's default-agent map needs to be keyed on (user_id, channel_id) on both the read and write paths to give each user their own per-channel default.

On main, both paths key on sender.platform_id, which the Discord/Slack adapters populate with the channel/conversation ID (it's needed downstream for the send path). The sender_user_id() helper existed but only the rate-limit/authz paths used it — routing reads and the four set_user_default writes didn't, and neither did the two broadcast lookups. Result: every per-user default ends up stored and looked up under the channel ID, so writes from different users on the same channel collide and reads return whoever wrote last.

The fix is localized to bridge.rs. Happy to send a PR if this is welcome — branch is fix/router-user-id-keying on my fork.

Expected Behavior

/agent <name> should set a default agent that is scoped to the invoking user on the current channel, with these properties:

  • User-scoped: User A's default on #general is independent of User B's default on #general. User B's /agent does not overwrite User A's.
  • Channel-scoped: A user's default on #general does not affect resolution on #other-channel. Each (user, channel) pair holds its own default.
  • Read path matches write path: the resolved default for a given mention is the same key that was written by the same user's last /agent on the same channel.

Steps to Reproduce

On main (pre-fix), in a workspace with at least two users sharing one channel:

Repro A — cross-user collision:

  1. As User A in #general: /agent assistant → confirmed set.
  2. As User B in #general: /agent coder → confirmed set.
  3. As User A in #general: <@bot> hello.
  4. Expected: routes to assistant (User A's default).
  5. Actual: routes to coder (User B's /agent clobbered User A's).

Repro B — cross-channel bleed:

  1. As User A in #general: /agent assistant.
  2. As User A in #other-channel: <@bot> hello.
  3. Expected: uses #other-channel's own default (or no default).
  4. Actual: picks up assistant from #general.

OpenFang Version

0.6.0

Operating System

macOS (Apple Silicon)

Logs / Screenshots

Reproducible without logs — both repros above are deterministic on a clean install. Happy to attach a daemon log capture of the collision if a triager requests one.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions