mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 20:20:42 +00:00
fix(agents): let id-embedded kind markers override prefix-derived kind
Matrix thread delivery encodes per-user DM targets as `room:@user:server` — the `room:` wrapper says "channel" but the embedded `@` id marker says "direct". The previous extractRequesterPeer gated the `@`/`!`/`#` heuristic on `!inferredKind`, so the prefix-derived kind won and a direct-kinded peer binding on the same user id was rejected by the kind-safety check in resolveFirstBoundAccountId. Cross-agent spawns whose target was bound as a direct Matrix peer could fall back to the caller account and send from the wrong identity. The fix removes the `!inferredKind` guard so id-embedded kind markers always have the final say — they are a more reliable signal than the delivery-target wrapper, because channel-side prefixes can wrap either a room or a user id. Regression test: sessions_spawn classifies Matrix `room:@user` targets as direct, not channel — the lifecycle suite now configures a conflicting `channel`-kinded binding on the same user id and asserts the `direct`-kinded binding wins when the caller's `agentTo` is `room:@user:example.org`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Gustavo Madeira Santana
parent
2efc727406
commit
09647e87a7
@@ -754,6 +754,62 @@ describe("openclaw-tools: subagents (sessions_spawn lifecycle)", () => {
|
||||
expect(spawnAccountId).toBe("bot-alpha-line");
|
||||
});
|
||||
|
||||
it("sessions_spawn classifies Matrix room:@user targets as direct, not channel", async () => {
|
||||
let spawnAccountId: string | undefined;
|
||||
const rawUserId = "@other-user:example.org";
|
||||
setSessionsSpawnConfigOverride({
|
||||
session: { mainKey: "main", scope: "per-sender" },
|
||||
messages: { queue: { debounceMs: 0 } },
|
||||
agents: { defaults: { subagents: { allowAgents: ["bot-alpha"] } } },
|
||||
bindings: [
|
||||
// A conflicting channel-kinded binding on the same peer id — must
|
||||
// not match a room:@user target because the embedded `@` marker
|
||||
// identifies this as a direct peer.
|
||||
{
|
||||
type: "route",
|
||||
agentId: "bot-alpha",
|
||||
match: {
|
||||
channel: "matrix",
|
||||
peer: { kind: "channel", id: rawUserId },
|
||||
accountId: "bot-alpha-wrong-kind",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "route",
|
||||
agentId: "bot-alpha",
|
||||
match: {
|
||||
channel: "matrix",
|
||||
peer: { kind: "direct", id: rawUserId },
|
||||
accountId: "bot-alpha-dm",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
setupSessionsSpawnGatewayMock({
|
||||
onAgentSubagentSpawn: (params) => {
|
||||
const rec = params as { accountId?: string } | undefined;
|
||||
spawnAccountId = rec?.accountId;
|
||||
},
|
||||
});
|
||||
|
||||
// Matrix thread delivery encodes per-user DM targets as `room:@user:server`.
|
||||
// The `room:` prefix must not override the embedded `@` direct-peer marker.
|
||||
const tool = await getSessionsSpawnTool({
|
||||
agentSessionKey: "main",
|
||||
agentChannel: "matrix",
|
||||
agentAccountId: "bot-beta",
|
||||
agentTo: `room:${rawUserId}`,
|
||||
});
|
||||
|
||||
const result = await tool.execute("call-room-at-user", {
|
||||
task: "do thing",
|
||||
agentId: "bot-alpha",
|
||||
cleanup: "keep",
|
||||
});
|
||||
expect(result.details).toMatchObject({ status: "accepted", runId: expect.any(String) });
|
||||
expect(spawnAccountId).toBe("bot-alpha-dm");
|
||||
});
|
||||
|
||||
it("sessions_spawn strips conversation: prefix for Teams-style targets", async () => {
|
||||
let spawnAccountId: string | undefined;
|
||||
const rawConversationId = "19:example-conversation@thread.v2";
|
||||
|
||||
@@ -345,6 +345,11 @@ function extractRequesterPeer(
|
||||
value = value.slice(prefix.length).trim();
|
||||
}
|
||||
if (value) {
|
||||
// Id-embedded kind markers (Matrix `!`/`@`, IRC `#`) win over prefix-derived
|
||||
// inference — channel-side wrappers can wrap either a room or a user id
|
||||
// (e.g. Matrix thread delivery encodes per-user DM targets as
|
||||
// `room:@user:server`), and the id itself is the authoritative signal for
|
||||
// what the peer actually is.
|
||||
if (value.startsWith("@")) {
|
||||
inferredKind = "direct";
|
||||
} else if (value.startsWith("!") || value.startsWith("#")) {
|
||||
|
||||
Reference in New Issue
Block a user