mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 23:20:43 +00:00
fix(agents): generic <word>: prefix peeler for delivery targets
MS Teams inbound context sets OriginatingTo to `conversation:<id>` while route bindings key on the bare conversationId. The previous hand-rolled prefix list (`room:`, `channel:`, `chat:`, `group:`, `user:`, `dm:`) missed `conversation:` (and any future channel-specific namespacing), so Teams cross-agent subagent spawns fell back to channel-only/caller account and posted from the wrong identity. extractRequesterPeer now uses a generic `^[a-z][a-z0-9_-]*:` regex to peel any lowercase-alpha token-colon prefix, looping until the raw peer id surfaces. Real-world peer ids never start with a lowercase-alpha token followed by `:` (Matrix uses `!`/`@`, IRC `#`, Slack/Discord/LINE alphanumerics, numeric Telegram/WhatsApp, or email-style `user@server`), so this is safe. Known prefixes are mapped to ChatType for peerKind inference (`conversation:`/`room:`/`channel:`/`chat:`/`thread:`/`topic:` → channel, `group:`/`team:` → group, `user:`/`dm:`/`pm:` → direct). Regression test: sessions_spawn strips `conversation:` prefix for Teams-style targets — a binding keyed on the raw conversation id resolves correctly when the caller's `agentTo` is `conversation:<id>`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Gustavo Madeira Santana
parent
3824ee0d6f
commit
2efc727406
@@ -754,6 +754,51 @@ describe("openclaw-tools: subagents (sessions_spawn lifecycle)", () => {
|
||||
expect(spawnAccountId).toBe("bot-alpha-line");
|
||||
});
|
||||
|
||||
it("sessions_spawn strips conversation: prefix for Teams-style targets", async () => {
|
||||
let spawnAccountId: string | undefined;
|
||||
const rawConversationId = "19:example-conversation@thread.v2";
|
||||
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: "msteams",
|
||||
peer: { kind: "channel", id: rawConversationId },
|
||||
accountId: "bot-alpha-teams",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
setupSessionsSpawnGatewayMock({
|
||||
onAgentSubagentSpawn: (params) => {
|
||||
const rec = params as { accountId?: string } | undefined;
|
||||
spawnAccountId = rec?.accountId;
|
||||
},
|
||||
});
|
||||
|
||||
// Teams inbound context sets OriginatingTo to `conversation:<id>`. With the
|
||||
// generic prefix peeler in extractRequesterPeer, the bound-account lookup
|
||||
// should still find the binding keyed on the raw conversation id.
|
||||
const tool = await getSessionsSpawnTool({
|
||||
agentSessionKey: "main",
|
||||
agentChannel: "msteams",
|
||||
agentAccountId: "bot-beta",
|
||||
agentTo: `conversation:${rawConversationId}`,
|
||||
});
|
||||
|
||||
const result = await tool.execute("call-teams-conversation", {
|
||||
task: "do thing",
|
||||
agentId: "bot-alpha",
|
||||
cleanup: "keep",
|
||||
});
|
||||
expect(result.details).toMatchObject({ status: "accepted", runId: expect.any(String) });
|
||||
expect(spawnAccountId).toBe("bot-alpha-teams");
|
||||
});
|
||||
|
||||
it("sessions_spawn preserves the caller's account for same-agent subagent spawns", async () => {
|
||||
let spawnAccountId: string | undefined;
|
||||
const room = "!someRoom:example.org";
|
||||
|
||||
@@ -308,6 +308,12 @@ const KIND_PREFIX_TO_CHAT_TYPE: Readonly<Record<string, ChatType>> = {
|
||||
"pm:": "direct",
|
||||
};
|
||||
|
||||
// Matches any `<alpha-token>:` prefix. Real-world peer ids (Matrix `!`/`@`,
|
||||
// IRC `#`, Slack/Discord/LINE alphanumerics, numeric Telegram/WhatsApp, or
|
||||
// email-style `user@server`) never start with a lowercase-alpha token followed
|
||||
// by `:`, so this cleanly peels namespace and kind prefixes without risking
|
||||
// the raw id itself. When a peeled token maps to a known ChatType, we also
|
||||
// record it as an inferred peerKind.
|
||||
const GENERIC_PREFIX_PATTERN = /^[a-z][a-z0-9_-]*:/i;
|
||||
|
||||
function extractRequesterPeer(
|
||||
|
||||
Reference in New Issue
Block a user