fix: normalize input peer.kind in resolveAgentRoute (#22730)

The input peer.kind from channel plugins was used as-is without
normalization via normalizeChatType(), while the binding side correctly
normalized. This caused "dm" !== "direct" mismatches in
matchesBindingScope, making plugins that use "dm" as peerKind fail to
match bindings configured with "direct".

Normalize both peer.kind and parentPeer.kind through normalizeChatType()
so that "dm" and "direct" are treated equivalently on both sides.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit b0c96702f5)
This commit is contained in:
Glucksberg
2026-02-23 02:14:07 +00:00
committed by Peter Steinberger
parent 24e52f53e4
commit fd7ca4c394
2 changed files with 34 additions and 2 deletions

View File

@@ -521,6 +521,30 @@ describe("backward compatibility: peer.kind dm → direct", () => {
expect(route.agentId).toBe("alex");
expect(route.matchedBy).toBe("binding.peer");
});
test("runtime dm peer.kind matches config direct binding (#22730)", () => {
const cfg: OpenClawConfig = {
bindings: [
{
agentId: "alex",
match: {
channel: "whatsapp",
// Config uses canonical "direct"
peer: { kind: "direct", id: "+15551234567" },
},
},
],
};
const route = resolveAgentRoute({
cfg,
channel: "whatsapp",
accountId: null,
// Plugin sends "dm" instead of "direct"
peer: { kind: "dm" as ChatType, id: "+15551234567" },
});
expect(route.agentId).toBe("alex");
expect(route.matchedBy).toBe("binding.peer");
});
});
describe("role-based agent routing", () => {

View File

@@ -291,7 +291,12 @@ function matchesBindingScope(match: NormalizedBindingMatch, scope: BindingScope)
export function resolveAgentRoute(input: ResolveAgentRouteInput): ResolvedAgentRoute {
const channel = normalizeToken(input.channel);
const accountId = normalizeAccountId(input.accountId);
const peer = input.peer ? { kind: input.peer.kind, id: normalizeId(input.peer.id) } : null;
const peer = input.peer
? {
kind: normalizeChatType(input.peer.kind) ?? input.peer.kind,
id: normalizeId(input.peer.id),
}
: null;
const guildId = normalizeId(input.guildId);
const teamId = normalizeId(input.teamId);
const memberRoleIds = input.memberRoleIds ?? [];
@@ -351,7 +356,10 @@ export function resolveAgentRoute(input: ResolveAgentRouteInput): ResolvedAgentR
}
// Thread parent inheritance: if peer (thread) didn't match, check parent peer binding
const parentPeer = input.parentPeer
? { kind: input.parentPeer.kind, id: normalizeId(input.parentPeer.id) }
? {
kind: normalizeChatType(input.parentPeer.kind) ?? input.parentPeer.kind,
id: normalizeId(input.parentPeer.id),
}
: null;
const baseScope = {
guildId,