diff --git a/src/agents/spawn-requester-origin.test.ts b/src/agents/spawn-requester-origin.test.ts index 8d1821ba923..646ea75895a 100644 --- a/src/agents/spawn-requester-origin.test.ts +++ b/src/agents/spawn-requester-origin.test.ts @@ -79,6 +79,41 @@ describe("resolveRequesterOriginForChild", () => { }); }); + it("keeps plugin-inferred channel kind for ids that start with direct marker characters", () => { + const to = "channel:@ops"; + const cfg = { + bindings: [ + { + type: "route", + agentId: "bot-alpha", + match: { + channel: "qa-channel", + peer: { + kind: "channel", + id: to, + }, + accountId: "bot-alpha-qa", + }, + }, + ], + } as OpenClawConfig; + + expect( + resolveRequesterOriginForChild({ + cfg, + targetAgentId: "bot-alpha", + requesterAgentId: "main", + requesterChannel: "qa-channel", + requesterAccountId: "bot-beta", + requesterTo: to, + }), + ).toMatchObject({ + channel: "qa-channel", + accountId: "bot-alpha-qa", + to, + }); + }); + it("still peels channel id plus kind wrappers before peer lookup", () => { const to = "line:group:U123example"; const cfg = { diff --git a/src/agents/spawn-requester-origin.ts b/src/agents/spawn-requester-origin.ts index eb7879484d8..06e9bb66472 100644 --- a/src/agents/spawn-requester-origin.ts +++ b/src/agents/spawn-requester-origin.ts @@ -44,11 +44,10 @@ export function extractRequesterPeer( if (!raw) { return {}; } - let inferredKind: ChatType | undefined; - if (channelId) { - const plugin = getChannelPlugin(channelId); - inferredKind = plugin?.messaging?.inferTargetChatType?.({ to: raw }) ?? undefined; - } + const pluginInferredKind = channelId + ? (getChannelPlugin(channelId)?.messaging?.inferTargetChatType?.({ to: raw }) ?? undefined) + : undefined; + let inferredKind: ChatType | undefined = pluginInferredKind; let value = raw; while (true) { const match = GENERIC_PREFIX_PATTERN.exec(value); @@ -64,9 +63,9 @@ export function extractRequesterPeer( } value = value.slice(prefix.length).trim(); } - if (value) { - // Id-embedded kind markers (Matrix `!`/`@`, IRC `#`) are authoritative - // because channel wrappers can wrap either room or user ids. + if (value && !pluginInferredKind) { + // Id-embedded kind markers (Matrix `!`/`@`, IRC `#`) are a fallback when + // the channel plugin cannot identify its own target grammar. if (value.startsWith("@")) { inferredKind = "direct"; } else if (value.startsWith("!") || value.startsWith("#")) {