#15663: fix(discord): normalize DM session keys for multi-agent setups 🤖
stale
size: S
Cluster:
Signal and Discord Fixes
## Summary
Fixes #15656 — Discord DM messages to non-default agents are silently dropped in multi-agent setups after v2026.2.12.
> **🤖 AI-Assisted PR** — Code written with Claude (AI), reviewed and tested by human on a production multi-agent deployment. Degree of testing: **fully tested in production** with 3 agents (main, work, fina) on separate Discord bot accounts.
## Problem
When using `per-channel-peer` dmScope with multiple agents bound to separate Discord bot accounts, DMs to non-default agents (e.g., `fina`, `work`) create phantom session entries with the wrong key format:
- **Expected**: `agent:fina:discord:direct:USERID`
- **Actual**: `agent:fina:discord:channel:USERID` (no model, no session file → silently dropped)
### Root Cause
`parseDiscordTarget(target, { defaultKind: "channel" })` in `resolveDiscordSession()` treats bare numeric user IDs as channel IDs. When outbound session routing processes a DM reply, it generates `discord:channel:USERID` keys. These phantom session entries get persisted via `ensureOutboundSessionEntry()` with no model and no session file, and subsequent messages get routed to them instead of the correct `discord:direct:USERID` sessions.
Additionally, the session key format changed from `discord:dm:` to `discord:direct:` between versions with no migration path for existing sessions.
## Fix
In `resolveSessionKey()` (`src/config/sessions/session-key.ts`), when `chatType` is `"direct"`, normalize misrouted Discord session keys:
1. **Legacy migration**: `discord:dm:USERID` → `discord:direct:USERID`
2. **Phantom fix**: `discord:channel:USERID` → `discord:direct:USERID` (only when sender ID matches the ID in the key, to avoid accidentally rewriting real channel keys)
This is a targeted, safe fix at the session key resolution layer. The sender ID guard ensures real `discord:channel:` keys for guild channels are never rewritten.
## Testing
- [x] Unit tests added (`session-key.test.ts`) covering all normalization cases and edge cases
- [x] Tested on production instance with 3 agents, 3 Discord bot accounts, `per-channel-peer` dmScope
- [x] Verified: main agent DMs still work, fina/work agent DMs now work, guild channel messages unaffected
## Notes
A more comprehensive upstream fix could also make `parseDiscordTarget` context-aware about DM vs channel targets, but that would be a larger change with wider blast radius. This PR takes the minimal safe approach.
Most Similar PRs
#17254: fix(discord): intercept text-based slash commands instead of forwar...
by robbyczgw-cla · 2026-02-15
71.3%
#9051: fix(sessions): respect dmScope config in CLI agent commands
by benleavett · 2026-02-04
71.1%
#20186: fix(discord): thread mediaLocalRoots through reply delivery path
by pvoo · 2026-02-18
71.0%
#15969: fix: per-thread session isolation for Slack DMs when replyToMode is...
by neeravmakwana · 2026-02-14
70.8%
#13580: fix(telegram): skip updateLastRoute when dmScope isolates DM sessions
by lailoo · 2026-02-10
70.7%
#20488: fix(discord): pass mediaLocalRoots to sendMessageDiscord
by olyashok · 2026-02-19
70.5%
#19615: fix(discord): include default account when sub-accounts are configured
by prue-starfield · 2026-02-18
70.5%
#8095: fix(sessions): include accountId in deliveryContext for outbound pe...
by codeslayer44 · 2026-02-03
70.3%
#22557: fix(discord): coerce exec approval approver IDs to string to preven...
by zwffff · 2026-02-21
69.7%
#6850: fix: support direct channel:account:peer format in session key extr...
by toboto · 2026-02-02
69.4%