#14888: fix(routing): normalize peer.kind in matchesPeer for symmetric comparison
size: S
Cluster:
Feishu Messaging Fixes
## Summary
Fixes asymmetric normalization in `matchesPeer()` that caused peer-based agent routing to always fail when a channel plugin passes `"dm"` instead of the canonical `"direct"`.
## Problem
`matchesPeer()` normalized the config binding\'s `peer.kind` via `normalizeChatType()` but compared it against the raw (unnormalized) incoming `peer.kind`. This meant:
- Config `"direct"` (normalized to `"direct"`) vs plugin `"dm"` (raw) → `"direct" === "dm"` → **always false**
- All peer-based routing silently fell through to the default agent
## Fix
Normalize `peer.kind` on **both sides** of the comparison in `matchesPeer()`:
```typescript
return kind === normalizeChatType(peer.kind) && id === peer.id;
```
This ensures `"dm"` and `"direct"` are treated as equivalent regardless of which side uses the alias.
## Tests
Added 3 test cases:
- Plugin passes `"dm"`, config uses `"direct"` → matches
- Config uses `"dm"`, plugin passes `"direct"` → matches
- `parentPeer` with `"dm"` alias → matches
All 23 tests pass.
Closes #14612
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR fixes peer-based routing matching by normalizing `peer.kind` on both sides of the `matchesPeer()` comparison so aliases like `"dm"` and the canonical `"direct"` compare symmetrically. It also adds unit tests covering config/runtime combinations (`dm`↔`direct`) and parent-thread inheritance behavior.
The change is localized to `src/routing/resolve-route.ts` and affects how bindings with `match.peer.kind` are selected during `resolveAgentRoute`, ensuring peer bindings no longer silently fall back to the default agent when an integration uses an alias.
<h3>Confidence Score: 4/5</h3>
- This PR is low risk and fixes a real routing mismatch, with one concern around test typing correctness.
- The production change is a minimal, targeted fix (normalizing runtime `peer.kind` during comparison) and is consistent with existing `normalizeChatType()` behavior. The added tests improve coverage, but they currently pass `kind: 'dm'` into an API typed as `ChatType`, which may break compilation if tests are type-checked or may mask type-contract drift.
- src/routing/resolve-route.test.ts
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#22757: fix(routing): normalize input peer.kind in resolveAgentRoute
by miloudbelarebia · 2026-02-21
88.0%
#21955: fix(line): remove redundant group:/room: prefix from buildPeerId (#...
by lailoo · 2026-02-20
77.0%
#15727: fix(routing): resolve channel default account instead of hardcoded ...
by FuseWeb · 2026-02-13
77.0%
#13477: routing: normalize account ID matching for agent bindings
by davidahmann · 2026-02-10
74.6%
#10643: fix(slack): classify D-prefix DMs correctly when channel_type disag...
by mcaxtr · 2026-02-06
74.2%
#9437: fix: normalize accountId in binding matching for consistent routing
by dbottme · 2026-02-05
74.0%
#20858: fix(hooks): normalize hook addresses to canonical provider:kind:id ...
by davidrudduck · 2026-02-19
73.0%
#23656: fix(routing): trust binding agentId even when not in agents.list
by SleuthCo · 2026-02-22
72.9%
#7224: feat(telegram): inherit forum topic bindings from parent group
by Buywatermelon · 2026-02-02
72.4%
#7868: Default DM sessions to per-channel scope (avoid webchat contention)
by Smile232323 · 2026-02-03
72.3%