mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 20:20:42 +00:00
fix(agents): normalize requester peer id before bound-account lookup
Delivery targets on Matrix (and other channels that namespace the `to`
field) arrive in `kind:<id>` form — for example `room:!abc:example.org`
— while route bindings store the raw peer id on `match.peer.id`
(`!abc:example.org`). Passing `ctx.agentTo` directly to
`resolveFirstBoundAccountId` caused exact peer matches to silently fail
and the lookup to fall through to channel-only or caller-account
fallback, so cross-agent spawns could still post as the wrong identity
when only a peer-specific binding was configured.
- src/agents/subagent-spawn.ts: strip known delivery-target prefixes
(`room:`, `channel:`, `chat:`, `user:`, `dm:`, `group:`, and the
channel-namespaced `${channelId}:`) from `requesterTo` before handing
it to `resolveFirstBoundAccountId`. The inferred `peerKind` still uses
the original `requesterTo` so channel plugins can apply their own
inference on the wire format.
Regression test in lifecycle suite:
- sessions_spawn strips channel-side prefixes from agentTo before the
bound-account lookup — a binding on the raw room id resolves correctly
even when the caller's `agentTo` is `room:<id>`.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Gustavo Madeira Santana
parent
6faff0c343
commit
9cfb6e1d85
@@ -652,6 +652,51 @@ describe("openclaw-tools: subagents (sessions_spawn lifecycle)", () => {
|
||||
expect(spawnAccountId).toBe("bot-alpha-room-a");
|
||||
});
|
||||
|
||||
it("sessions_spawn strips channel-side prefixes from agentTo before bound-account lookup", async () => {
|
||||
let spawnAccountId: string | undefined;
|
||||
const rawRoomId = "!exampleRoomId:example.org";
|
||||
setSessionsSpawnConfigOverride({
|
||||
session: { mainKey: "main", scope: "per-sender" },
|
||||
messages: { queue: { debounceMs: 0 } },
|
||||
agents: { defaults: { subagents: { allowAgents: ["bot-alpha"] } } },
|
||||
bindings: [
|
||||
{
|
||||
type: "route",
|
||||
agentId: "bot-alpha",
|
||||
match: {
|
||||
channel: "matrix",
|
||||
peer: { kind: "channel", id: rawRoomId },
|
||||
accountId: "bot-alpha",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
setupSessionsSpawnGatewayMock({
|
||||
onAgentSubagentSpawn: (params) => {
|
||||
const rec = params as { accountId?: string } | undefined;
|
||||
spawnAccountId = rec?.accountId;
|
||||
},
|
||||
});
|
||||
|
||||
// agentTo arrives in delivery-target format (room:<id>), while the binding
|
||||
// stores the raw id. Without prefix normalization the exact peer match
|
||||
// would silently fail and the caller account would leak to the child.
|
||||
const tool = await getSessionsSpawnTool({
|
||||
agentSessionKey: "main",
|
||||
agentChannel: "matrix",
|
||||
agentAccountId: "bot-beta",
|
||||
agentTo: `room:${rawRoomId}`,
|
||||
});
|
||||
|
||||
const result = await tool.execute("call-prefixed-to", {
|
||||
task: "do thing",
|
||||
agentId: "bot-alpha",
|
||||
cleanup: "keep",
|
||||
});
|
||||
expect(result.details).toMatchObject({ status: "accepted", runId: expect.any(String) });
|
||||
expect(spawnAccountId).toBe("bot-alpha");
|
||||
});
|
||||
|
||||
it("sessions_spawn preserves the caller's account for same-agent subagent spawns", async () => {
|
||||
let spawnAccountId: string | undefined;
|
||||
const room = "!someRoom:example.org";
|
||||
|
||||
@@ -287,6 +287,10 @@ function summarizeError(err: unknown): string {
|
||||
return "error";
|
||||
}
|
||||
|
||||
// Delivery targets carry a channel-side prefix (e.g. Matrix uses `room:<roomId>`;
|
||||
// LINE uses `line:group:<id>`), but route bindings store raw peer ids on
|
||||
// `match.peer.id`. Peel the `<channel>:` namespace first, then loop over generic
|
||||
// target-kind prefixes so the raw peer id surfaces.
|
||||
const KIND_PREFIX_TO_CHAT_TYPE: Readonly<Record<string, ChatType>> = {
|
||||
"room:": "channel",
|
||||
"channel:": "channel",
|
||||
|
||||
Reference in New Issue
Block a user