From 73f36b0c806c8178740b618a04ae39599c38ba55 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 20 Apr 2026 23:47:49 +0100 Subject: [PATCH] test: use synthetic outbound dispatch fixtures --- ...sage-action-runner.plugin-dispatch.test.ts | 234 +++++++++--------- .../outbound/outbound-session.test-helpers.ts | 161 ++++++------ src/infra/outbound/outbound-session.test.ts | 162 ++++++------ src/infra/outbound/target-resolver.test.ts | 2 +- 4 files changed, 280 insertions(+), 279 deletions(-) diff --git a/src/infra/outbound/message-action-runner.plugin-dispatch.test.ts b/src/infra/outbound/message-action-runner.plugin-dispatch.test.ts index 0eed0b894fb..6dc9b6ae2a9 100644 --- a/src/infra/outbound/message-action-runner.plugin-dispatch.test.ts +++ b/src/infra/outbound/message-action-runner.plugin-dispatch.test.ts @@ -41,7 +41,7 @@ vi.mock("./outbound-session.js", () => ({ vi.mock("../../channels/plugins/bootstrap-registry.js", () => ({ getBootstrapChannelPlugin: (id: string) => - id === "feishu" + id === "actionhub" ? { actions: { messageActionTargetAliases: { @@ -164,14 +164,14 @@ describe("runMessageAction plugin dispatch", () => { }), ); - const feishuLikePlugin: ChannelPlugin = { - id: "feishu", + const actionHubPlugin: ChannelPlugin = { + id: "actionhub", meta: { - id: "feishu", - label: "Feishu", - selectionLabel: "Feishu", - docsPath: "/channels/feishu", - blurb: "Feishu action dispatch test plugin.", + id: "actionhub", + label: "Action Hub", + selectionLabel: "Action Hub", + docsPath: "/channels/actionhub", + blurb: "Action Hub action dispatch test plugin.", }, capabilities: { chatTypes: ["direct", "channel"] }, config: createAlwaysConfiguredPluginConfig(), @@ -192,9 +192,9 @@ describe("runMessageAction plugin dispatch", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "feishu", + pluginId: "actionhub", source: "test", - plugin: feishuLikePlugin, + plugin: actionHubPlugin, }, ]), ); @@ -207,18 +207,18 @@ describe("runMessageAction plugin dispatch", () => { vi.unstubAllEnvs(); }); - it("dispatches messageId/chatId-based Feishu actions through the shared runner", async () => { + it("dispatches messageId/chatId-based plugin actions through the shared runner", async () => { await runMessageAction({ cfg: { channels: { - feishu: { + actionhub: { enabled: true, }, }, } as OpenClawConfig, action: "pin", params: { - channel: "feishu", + channel: "actionhub", messageId: "om_123", }, dryRun: false, @@ -227,14 +227,14 @@ describe("runMessageAction plugin dispatch", () => { await runMessageAction({ cfg: { channels: { - feishu: { + actionhub: { enabled: true, }, }, } as OpenClawConfig, action: "list-pins", params: { - channel: "feishu", + channel: "actionhub", chatId: "oc_123", }, dryRun: false, @@ -268,14 +268,14 @@ describe("runMessageAction plugin dispatch", () => { await runMessageAction({ cfg: { channels: { - feishu: { + actionhub: { enabled: true, }, }, } as OpenClawConfig, action: "pin", params: { - channel: "feishu", + channel: "actionhub", messageId: "om_123", }, defaultAccountId: "ops", @@ -285,7 +285,7 @@ describe("runMessageAction plugin dispatch", () => { agentId: "alpha", toolContext: { currentChannelId: "oc_123", - currentChannelProvider: "feishu", + currentChannelProvider: "actionhub", currentThreadTs: "thread-456", currentMessageId: "msg-789", }, @@ -303,7 +303,7 @@ describe("runMessageAction plugin dispatch", () => { mediaLocalRoots: expect.arrayContaining([expectedWorkspaceRoot]), toolContext: expect.objectContaining({ currentChannelId: "oc_123", - currentChannelProvider: "feishu", + currentChannelProvider: "actionhub", currentThreadTs: "thread-456", currentMessageId: "msg-789", }), @@ -319,13 +319,13 @@ describe("runMessageAction plugin dispatch", () => { }), ); const gatewayPlugin: ChannelPlugin = { - id: "whatsapp", + id: "gatewaychat", meta: { - id: "whatsapp", - label: "WhatsApp", - selectionLabel: "WhatsApp", - docsPath: "/channels/whatsapp", - blurb: "WhatsApp reaction test plugin.", + id: "gatewaychat", + label: "Gateway Chat", + selectionLabel: "Gateway Chat", + docsPath: "/channels/gatewaychat", + blurb: "Gateway Chat reaction test plugin.", }, capabilities: { chatTypes: ["direct"], reactions: true }, config: createAlwaysConfiguredPluginConfig(), @@ -339,7 +339,7 @@ describe("runMessageAction plugin dispatch", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "whatsapp", + pluginId: "gatewaychat", source: "test", plugin: gatewayPlugin, }, @@ -353,14 +353,14 @@ describe("runMessageAction plugin dispatch", () => { const result = await runMessageAction({ cfg: { channels: { - whatsapp: { + gatewaychat: { enabled: true, }, }, } as OpenClawConfig, action: "react", params: { - channel: "whatsapp", + channel: "gatewaychat", to: "+15551234567", chatJid: "+15551234567", messageId: "wamid.1", @@ -371,7 +371,7 @@ describe("runMessageAction plugin dispatch", () => { sessionId: "session-123", agentId: "alpha", toolContext: { - currentChannelProvider: "whatsapp", + currentChannelProvider: "gatewaychat", currentMessageId: "wamid.1", }, gateway: { @@ -385,14 +385,14 @@ describe("runMessageAction plugin dispatch", () => { expect.objectContaining({ method: "message.action", params: expect.objectContaining({ - channel: "whatsapp", + channel: "gatewaychat", action: "react", requesterSenderId: "trusted-user", sessionKey: "agent:alpha:main", sessionId: "session-123", agentId: "alpha", toolContext: expect.objectContaining({ - currentChannelProvider: "whatsapp", + currentChannelProvider: "gatewaychat", currentMessageId: "wamid.1", }), idempotencyKey: "idem-gateway-action", @@ -402,7 +402,7 @@ describe("runMessageAction plugin dispatch", () => { expect(handleAction).not.toHaveBeenCalled(); expect(result).toMatchObject({ kind: "action", - channel: "whatsapp", + channel: "gatewaychat", action: "react", handledBy: "plugin", payload: { @@ -420,13 +420,13 @@ describe("runMessageAction plugin dispatch", () => { }), ); const policyPlugin: ChannelPlugin = { - id: "feishu", + id: "policydest", meta: { - id: "feishu", - label: "Feishu", - selectionLabel: "Feishu", - docsPath: "/channels/feishu", - blurb: "Feishu policy test plugin.", + id: "policydest", + label: "Policy Destination", + selectionLabel: "Policy Destination", + docsPath: "/channels/policydest", + blurb: "Policy destination test plugin.", }, capabilities: { chatTypes: ["direct", "channel"], media: true }, config: createAlwaysConfiguredPluginConfig(), @@ -445,7 +445,7 @@ describe("runMessageAction plugin dispatch", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "feishu", + pluginId: "policydest", source: "test", plugin: policyPlugin, }, @@ -456,10 +456,10 @@ describe("runMessageAction plugin dispatch", () => { cfg: { tools: { allow: ["read"] }, channels: { - feishu: { + policydest: { enabled: true, }, - whatsapp: { + requestchat: { groups: { ops: { toolsBySender: { @@ -474,13 +474,13 @@ describe("runMessageAction plugin dispatch", () => { } as OpenClawConfig, action: "send", params: { - channel: "feishu", + channel: "policydest", target: "oc_123", message: "hello", media: "/tmp/host.png", }, requesterSenderId: "trusted-user", - sessionKey: "agent:alpha:whatsapp:group:ops", + sessionKey: "agent:alpha:requestchat:group:ops", dryRun: false, }); @@ -497,13 +497,13 @@ describe("runMessageAction plugin dispatch", () => { }), ); const policyPlugin: ChannelPlugin = { - id: "feishu", + id: "policydest", meta: { - id: "feishu", - label: "Feishu", - selectionLabel: "Feishu", - docsPath: "/channels/feishu", - blurb: "Feishu username policy test plugin.", + id: "policydest", + label: "Policy Destination", + selectionLabel: "Policy Destination", + docsPath: "/channels/policydest", + blurb: "Policy destination username test plugin.", }, capabilities: { chatTypes: ["direct", "channel"], media: true }, config: createAlwaysConfiguredPluginConfig(), @@ -522,7 +522,7 @@ describe("runMessageAction plugin dispatch", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "feishu", + pluginId: "policydest", source: "test", plugin: policyPlugin, }, @@ -533,10 +533,10 @@ describe("runMessageAction plugin dispatch", () => { cfg: { tools: { allow: ["read"] }, channels: { - feishu: { + policydest: { enabled: true, }, - whatsapp: { + requestchat: { groups: { ops: { toolsBySender: { @@ -551,13 +551,13 @@ describe("runMessageAction plugin dispatch", () => { } as OpenClawConfig, action: "send", params: { - channel: "feishu", + channel: "policydest", target: "oc_123", message: "hello", media: "/tmp/host.png", }, requesterSenderUsername: "alice_u", - sessionKey: "agent:alpha:whatsapp:group:ops", + sessionKey: "agent:alpha:requestchat:group:ops", dryRun: false, }); @@ -574,13 +574,13 @@ describe("runMessageAction plugin dispatch", () => { }), ); const policyPlugin: ChannelPlugin = { - id: "feishu", + id: "policydest", meta: { - id: "feishu", - label: "Feishu", - selectionLabel: "Feishu", - docsPath: "/channels/feishu", - blurb: "Feishu account policy test plugin.", + id: "policydest", + label: "Policy Destination", + selectionLabel: "Policy Destination", + docsPath: "/channels/policydest", + blurb: "Policy destination account test plugin.", }, capabilities: { chatTypes: ["direct", "channel"], media: true }, config: createAlwaysConfiguredPluginConfig(), @@ -599,7 +599,7 @@ describe("runMessageAction plugin dispatch", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "feishu", + pluginId: "policydest", source: "test", plugin: policyPlugin, }, @@ -610,10 +610,10 @@ describe("runMessageAction plugin dispatch", () => { cfg: { tools: { allow: ["read"] }, channels: { - feishu: { + policydest: { enabled: true, }, - whatsapp: { + requestchat: { accounts: { source: { groups: { @@ -643,7 +643,7 @@ describe("runMessageAction plugin dispatch", () => { } as OpenClawConfig, action: "send", params: { - channel: "feishu", + channel: "policydest", accountId: "destination", target: "oc_123", message: "hello", @@ -651,7 +651,7 @@ describe("runMessageAction plugin dispatch", () => { }, requesterAccountId: "source", requesterSenderId: "trusted-user", - sessionKey: "agent:alpha:whatsapp:group:ops", + sessionKey: "agent:alpha:requestchat:group:ops", dryRun: false, }); @@ -669,13 +669,13 @@ describe("runMessageAction plugin dispatch", () => { }), ); const policyPlugin: ChannelPlugin = { - id: "whatsapp", + id: "policychat", meta: { - id: "whatsapp", - label: "WhatsApp", - selectionLabel: "WhatsApp", - docsPath: "/channels/whatsapp", - blurb: "WhatsApp account policy fallback test plugin.", + id: "policychat", + label: "Policy Chat", + selectionLabel: "Policy Chat", + docsPath: "/channels/policychat", + blurb: "Policy chat account fallback test plugin.", }, capabilities: { chatTypes: ["direct", "channel"], media: true }, config: createAlwaysConfiguredPluginConfig(), @@ -694,7 +694,7 @@ describe("runMessageAction plugin dispatch", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "whatsapp", + pluginId: "policychat", source: "test", plugin: policyPlugin, }, @@ -705,7 +705,7 @@ describe("runMessageAction plugin dispatch", () => { cfg: { tools: { allow: ["read"] }, channels: { - whatsapp: { + policychat: { enabled: true, accounts: { source: { @@ -725,14 +725,14 @@ describe("runMessageAction plugin dispatch", () => { } as OpenClawConfig, action: "send", params: { - channel: "whatsapp", + channel: "policychat", accountId: "source", target: "group:ops", message: "hello", media: "/tmp/host.png", }, requesterSenderId: "trusted-user", - sessionKey: "agent:alpha:whatsapp:group:ops", + sessionKey: "agent:alpha:policychat:group:ops", dryRun: false, }); @@ -824,7 +824,7 @@ describe("runMessageAction plugin dispatch", () => { }); }); - describe("telegram plugin poll forwarding", () => { + describe("poll plugin forwarding", () => { const handleAction = vi.fn(async ({ params }: { params: Record }) => jsonResult({ ok: true, @@ -839,10 +839,10 @@ describe("runMessageAction plugin dispatch", () => { }), ); - const telegramPollPlugin = createPollForwardingPlugin({ - pluginId: "telegram", - label: "Telegram", - blurb: "Telegram poll forwarding test plugin.", + const pollChatPlugin = createPollForwardingPlugin({ + pluginId: "pollchat", + label: "Poll Chat", + blurb: "Poll chat forwarding test plugin.", handleAction, }); @@ -850,9 +850,9 @@ describe("runMessageAction plugin dispatch", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "telegram", + pluginId: "pollchat", source: "test", - plugin: telegramPollPlugin, + plugin: pollChatPlugin, }, ]), ); @@ -864,19 +864,19 @@ describe("runMessageAction plugin dispatch", () => { vi.clearAllMocks(); }); - it("forwards telegram poll params through plugin dispatch", async () => { + it("forwards poll params through plugin dispatch", async () => { const result = await runMessageAction({ cfg: { channels: { - telegram: { + pollchat: { botToken: "tok", }, }, } as OpenClawConfig, action: "poll", params: { - channel: "telegram", - target: "telegram:123", + channel: "pollchat", + target: "pollchat:123", pollQuestion: "Lunch?", pollOption: ["Pizza", "Sushi"], pollDurationSeconds: 120, @@ -891,9 +891,9 @@ describe("runMessageAction plugin dispatch", () => { expect(handleAction).toHaveBeenCalledWith( expect.objectContaining({ action: "poll", - channel: "telegram", + channel: "pollchat", params: expect.objectContaining({ - to: "telegram:123", + to: "pollchat:123", pollQuestion: "Lunch?", pollOption: ["Pizza", "Sushi"], pollDurationSeconds: 120, @@ -905,7 +905,7 @@ describe("runMessageAction plugin dispatch", () => { expect(result.payload).toMatchObject({ ok: true, forwarded: { - to: "telegram:123", + to: "pollchat:123", pollQuestion: "Lunch?", pollOption: ["Pizza", "Sushi"], pollDurationSeconds: 120, @@ -930,10 +930,10 @@ describe("runMessageAction plugin dispatch", () => { }), ); - const discordPollPlugin = createPollForwardingPlugin({ - pluginId: "discord", - label: "Discord", - blurb: "Discord plugin-owned poll test plugin.", + const guildPollPlugin = createPollForwardingPlugin({ + pluginId: "guildchat", + label: "Guild Chat", + blurb: "Guild chat plugin-owned poll test plugin.", handleAction, }); @@ -941,9 +941,9 @@ describe("runMessageAction plugin dispatch", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "discord", + pluginId: "guildchat", source: "test", - plugin: discordPollPlugin, + plugin: guildPollPlugin, }, ]), ); @@ -955,18 +955,18 @@ describe("runMessageAction plugin dispatch", () => { vi.clearAllMocks(); }); - it("lets non-telegram plugins own extra poll fields", async () => { + it("lets other plugins own extra poll fields", async () => { const result = await runMessageAction({ cfg: { channels: { - discord: { + guildchat: { token: "tok", }, }, } as OpenClawConfig, action: "poll", params: { - channel: "discord", + channel: "guildchat", target: "channel:123", pollQuestion: "Lunch?", pollOption: ["Pizza", "Sushi"], @@ -981,7 +981,7 @@ describe("runMessageAction plugin dispatch", () => { expect(handleAction).toHaveBeenCalledWith( expect.objectContaining({ action: "poll", - channel: "discord", + channel: "guildchat", params: expect.objectContaining({ to: "channel:123", pollQuestion: "Lunch?", @@ -1003,13 +1003,13 @@ describe("runMessageAction plugin dispatch", () => { ); const componentsPlugin: ChannelPlugin = { - id: "discord", + id: "componentchat", meta: { - id: "discord", - label: "Discord", - selectionLabel: "Discord", - docsPath: "/channels/discord", - blurb: "Discord components send test plugin.", + id: "componentchat", + label: "Component Chat", + selectionLabel: "Component Chat", + docsPath: "/channels/componentchat", + blurb: "Component chat send test plugin.", }, capabilities: { chatTypes: ["direct"] }, config: createAlwaysConfiguredPluginConfig({}), @@ -1024,7 +1024,7 @@ describe("runMessageAction plugin dispatch", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "discord", + pluginId: "componentchat", source: "test", plugin: componentsPlugin, }, @@ -1047,7 +1047,7 @@ describe("runMessageAction plugin dispatch", () => { cfg: {} as OpenClawConfig, action: "send", params: { - channel: "discord", + channel: "componentchat", target: "channel:123", message: "hi", components: JSON.stringify(components), @@ -1066,7 +1066,7 @@ describe("runMessageAction plugin dispatch", () => { cfg: {} as OpenClawConfig, action: "send", params: { - channel: "discord", + channel: "componentchat", target: "channel:123", message: "hi", components: "{not-json}", @@ -1082,13 +1082,13 @@ describe("runMessageAction plugin dispatch", () => { describe("accountId defaults", () => { const handleAction = vi.fn(async () => jsonResult({ ok: true })); const accountPlugin: ChannelPlugin = { - id: "discord", + id: "accountchat", meta: { - id: "discord", - label: "Discord", - selectionLabel: "Discord", - docsPath: "/channels/discord", - blurb: "Discord test plugin.", + id: "accountchat", + label: "Account Chat", + selectionLabel: "Account Chat", + docsPath: "/channels/accountchat", + blurb: "Account chat test plugin.", }, capabilities: { chatTypes: ["direct"] }, config: { @@ -1105,7 +1105,7 @@ describe("runMessageAction plugin dispatch", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "discord", + pluginId: "accountchat", source: "test", plugin: accountPlugin, }, @@ -1133,7 +1133,7 @@ describe("runMessageAction plugin dispatch", () => { args: { cfg: { bindings: [ - { agentId: "agent-b", match: { channel: "discord", accountId: "account-b" } }, + { agentId: "agent-b", match: { channel: "accountchat", accountId: "account-b" } }, ], } as OpenClawConfig, agentId: "agent-b", @@ -1145,7 +1145,7 @@ describe("runMessageAction plugin dispatch", () => { ...args, action: "send", params: { - channel: "discord", + channel: "accountchat", target: "channel:123", message: "hi", }, diff --git a/src/infra/outbound/outbound-session.test-helpers.ts b/src/infra/outbound/outbound-session.test-helpers.ts index b5c1a267fa2..a17b4859e8d 100644 --- a/src/infra/outbound/outbound-session.test-helpers.ts +++ b/src/infra/outbound/outbound-session.test-helpers.ts @@ -79,15 +79,14 @@ function buildThreadedChannelRoute(params: { }; } -function parseTelegramTargetForTest(raw: string): { +function parseForumTargetForTest(raw: string): { chatId: string; messageThreadId?: number; chatType: "direct" | "group" | "unknown"; } { const trimmed = raw .trim() - .replace(/^telegram:/i, "") - .replace(/^tg:/i, "") + .replace(/^forum:/i, "") .replace(/^group:/i, ""); const prefixedTopic = /^([^:]+):topic:(\d+)$/i.exec(trimmed); if (prefixedTopic) { @@ -104,7 +103,7 @@ function parseTelegramTargetForTest(raw: string): { }; } -function parseTelegramThreadIdForTest(threadId?: string | number | null): number | undefined { +function parseForumThreadIdForTest(threadId?: string | number | null): number | undefined { const normalized = normalizeOutboundThreadId(threadId); if (!normalized) { return undefined; @@ -116,26 +115,24 @@ function parseTelegramThreadIdForTest(threadId?: string | number | null): number return Number.parseInt(topicMatch[1], 10); } -function buildTelegramGroupPeerIdForTest(chatId: string, messageThreadId?: number): string { +function buildForumGroupPeerIdForTest(chatId: string, messageThreadId?: number): string { return messageThreadId ? `${chatId}:topic:${messageThreadId}` : chatId; } -function resolveTelegramOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { - const parsed = parseTelegramTargetForTest(params.target); +function resolveForumOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { + const parsed = parseForumTargetForTest(params.target); const chatId = parsed.chatId.trim(); if (!chatId) { return null; } - const resolvedThreadId = parsed.messageThreadId ?? parseTelegramThreadIdForTest(params.threadId); + const resolvedThreadId = parsed.messageThreadId ?? parseForumThreadIdForTest(params.threadId); const isGroup = parsed.chatType === "group" || (parsed.chatType === "unknown" && params.resolvedTarget?.kind !== undefined && params.resolvedTarget.kind !== "user"); const peerId = - isGroup && resolvedThreadId - ? buildTelegramGroupPeerIdForTest(chatId, resolvedThreadId) - : chatId; + isGroup && resolvedThreadId ? buildForumGroupPeerIdForTest(chatId, resolvedThreadId) : chatId; const peer: RoutePeer = { kind: isGroup ? "group" : "direct", id: peerId, @@ -144,67 +141,71 @@ function resolveTelegramOutboundSessionRouteForTest(params: ChannelOutboundSessi return buildChannelOutboundSessionRoute({ cfg: params.cfg, agentId: params.agentId, - channel: "telegram", + channel: "forum", accountId: params.accountId, peer, chatType: "group", - from: `telegram:group:${peerId}`, - to: `telegram:${chatId}`, + from: `forum:group:${peerId}`, + to: `forum:${chatId}`, ...(resolvedThreadId !== undefined ? { threadId: resolvedThreadId } : {}), }); } return buildThreadedChannelRoute({ cfg: params.cfg, agentId: params.agentId, - channel: "telegram", + channel: "forum", accountId: params.accountId, peer, chatType: "direct", from: resolvedThreadId !== undefined - ? `telegram:${chatId}:topic:${resolvedThreadId}` - : `telegram:${chatId}`, - to: `telegram:${chatId}`, + ? `forum:${chatId}:topic:${resolvedThreadId}` + : `forum:${chatId}`, + to: `forum:${chatId}`, threadId: resolvedThreadId, }); } -function resolveSlackOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { +function resolveWorkspaceOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { const trimmed = params.target.trim(); if (!trimmed) { return null; } const lower = normalizeLowercaseStringOrEmpty(trimmed); - const rawId = stripTargetKindPrefix(stripChannelTargetPrefix(trimmed, "slack")); + const rawId = stripTargetKindPrefix(stripChannelTargetPrefix(trimmed, "workspace")); if (!rawId) { return null; } const normalizedId = normalizeLowercaseStringOrEmpty(rawId); - const isDm = lower.startsWith("user:") || lower.startsWith("slack:") || /^u/i.test(rawId); + const isDm = lower.startsWith("user:") || lower.startsWith("workspace:") || /^u/i.test(rawId); + const workspaceConfig = params.cfg.channels?.workspace as + | { dm?: { groupChannels?: unknown[] } } + | undefined; const isGroupChannel = /^g/i.test(rawId) && - params.cfg.channels?.slack?.dm?.groupChannels?.some( - (candidate) => normalizeLowercaseStringOrEmpty(String(candidate)) === normalizedId, - ) === true; + Array.isArray(workspaceConfig?.dm?.groupChannels) && + workspaceConfig.dm.groupChannels.some( + (candidate: unknown) => normalizeLowercaseStringOrEmpty(String(candidate)) === normalizedId, + ); const peerKind: RoutePeer["kind"] = isDm ? "direct" : isGroupChannel ? "group" : "channel"; return buildThreadedChannelRoute({ cfg: params.cfg, agentId: params.agentId, - channel: "slack", + channel: "workspace", accountId: params.accountId, peer: { kind: peerKind, id: normalizedId }, chatType: peerKind === "direct" ? "direct" : peerKind === "group" ? "group" : "channel", from: isDm - ? `slack:${rawId}` + ? `workspace:${rawId}` : isGroupChannel - ? `slack:group:${rawId}` - : `slack:channel:${rawId}`, + ? `workspace:group:${rawId}` + : `workspace:channel:${rawId}`, to: isDm ? `user:${rawId}` : `channel:${rawId}`, threadId: params.replyToId ?? params.threadId ?? undefined, }); } -function resolveDiscordOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { +function resolveGuildChatOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { const trimmed = params.target.trim(); if (!trimmed) { return null; @@ -215,16 +216,16 @@ function resolveDiscordOutboundSessionRouteForTest(params: ChannelOutboundSessio kind = "user"; } else if (resolvedKind === "channel" || resolvedKind === "group") { kind = "channel"; - } else if (/^user:/i.test(trimmed) || /^discord:/i.test(trimmed) || /^<@!?/.test(trimmed)) { + } else if (/^user:/i.test(trimmed) || /^guildchat:/i.test(trimmed) || /^<@!?/.test(trimmed)) { kind = "user"; } else if (/^channel:/i.test(trimmed)) { kind = "channel"; } else if (/^\d+$/u.test(trimmed)) { - throw new Error("Ambiguous Discord recipient"); + throw new Error("Ambiguous Guild Chat recipient"); } else { kind = "channel"; } - const rawId = stripTargetKindPrefix(stripChannelTargetPrefix(trimmed, "discord")); + const rawId = stripTargetKindPrefix(stripChannelTargetPrefix(trimmed, "guildchat")); if (!rawId) { return null; } @@ -235,43 +236,43 @@ function resolveDiscordOutboundSessionRouteForTest(params: ChannelOutboundSessio return buildThreadedChannelRoute({ cfg: params.cfg, agentId: params.agentId, - channel: "discord", + channel: "guildchat", accountId: params.accountId, peer, chatType: kind === "user" ? "direct" : "channel", - from: kind === "user" ? `discord:${rawId}` : `discord:channel:${rawId}`, + from: kind === "user" ? `guildchat:${rawId}` : `guildchat:channel:${rawId}`, to: kind === "user" ? `user:${rawId}` : `channel:${rawId}`, threadId: params.threadId ?? undefined, useSuffix: false, }); } -function resolveMattermostOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { +function resolveBoardChatOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { const trimmed = params.target.trim(); if (!trimmed) { return null; } const isUser = params.resolvedTarget?.kind === "user" || /^user:/i.test(trimmed); - const rawId = stripTargetKindPrefix(stripChannelTargetPrefix(trimmed, "mattermost")); + const rawId = stripTargetKindPrefix(stripChannelTargetPrefix(trimmed, "boardchat")); if (!rawId) { return null; } return buildThreadedChannelRoute({ cfg: params.cfg, agentId: params.agentId, - channel: "mattermost", + channel: "boardchat", accountId: params.accountId, peer: { kind: isUser ? "direct" : "channel", id: rawId }, chatType: isUser ? "direct" : "channel", - from: isUser ? `mattermost:${rawId}` : `mattermost:channel:${rawId}`, + from: isUser ? `boardchat:${rawId}` : `boardchat:channel:${rawId}`, to: isUser ? `user:${rawId}` : `channel:${rawId}`, threadId: params.replyToId ?? params.threadId ?? undefined, }); } -function resolveWhatsAppOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { +function resolveMobileChatOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { const normalized = normalizeOptionalLowercaseString( - stripChannelTargetPrefix(params.target, "whatsapp"), + stripChannelTargetPrefix(params.target, "mobilechat"), ); if (!normalized) { return null; @@ -280,7 +281,7 @@ function resolveWhatsAppOutboundSessionRouteForTest(params: ChannelOutboundSessi return buildChannelOutboundSessionRoute({ cfg: params.cfg, agentId: params.agentId, - channel: "whatsapp", + channel: "mobilechat", accountId: params.accountId, peer: { kind: isGroup ? "group" : "direct", id: normalized }, chatType: isGroup ? "group" : "direct", @@ -309,8 +310,8 @@ function resolveMatrixOutboundSessionRouteForTest(params: ChannelOutboundSession }); } -function resolveMSTeamsOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { - const trimmed = stripChannelTargetPrefix(params.target, "msteams", "teams"); +function resolveMeetingChatOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { + const trimmed = stripChannelTargetPrefix(params.target, "meetingchat", "meet"); if (!trimmed) { return null; } @@ -326,21 +327,21 @@ function resolveMSTeamsOutboundSessionRouteForTest(params: ChannelOutboundSessio return buildChannelOutboundSessionRoute({ cfg: params.cfg, agentId: params.agentId, - channel: "msteams", + channel: "meetingchat", accountId: params.accountId, peer: { kind: peerKind, id: conversationId }, chatType: peerKind, from: isUser - ? `msteams:${conversationId}` + ? `meetingchat:${conversationId}` : isChannel - ? `msteams:channel:${conversationId}` - : `msteams:group:${conversationId}`, + ? `meetingchat:channel:${conversationId}` + : `meetingchat:group:${conversationId}`, to: isUser ? `user:${conversationId}` : `conversation:${conversationId}`, }); } -function resolveFeishuOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { - let trimmed = stripChannelTargetPrefix(params.target, "feishu", "lark"); +function resolveCollabChatOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { + let trimmed = stripChannelTargetPrefix(params.target, "collabchat", "collab"); if (!trimmed) { return null; } @@ -360,11 +361,11 @@ function resolveFeishuOutboundSessionRouteForTest(params: ChannelOutboundSession return buildChannelOutboundSessionRoute({ cfg: params.cfg, agentId: params.agentId, - channel: "feishu", + channel: "collabchat", accountId: params.accountId, peer: { kind: isGroup ? "group" : "direct", id: trimmed }, chatType: isGroup ? "group" : "direct", - from: isGroup ? `feishu:group:${trimmed}` : `feishu:${trimmed}`, + from: isGroup ? `collabchat:group:${trimmed}` : `collabchat:${trimmed}`, to: trimmed, }); } @@ -390,8 +391,8 @@ function resolveNextcloudTalkOutboundSessionRouteForTest( }); } -function resolveBlueBubblesOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { - const stripped = stripChannelTargetPrefix(params.target, "bluebubbles"); +function resolveLocalChatOutboundSessionRouteForTest(params: ChannelOutboundSessionRouteParams) { + const stripped = stripChannelTargetPrefix(params.target, "localchat"); if (!stripped) { return null; } @@ -405,12 +406,12 @@ function resolveBlueBubblesOutboundSessionRouteForTest(params: ChannelOutboundSe return buildChannelOutboundSessionRoute({ cfg: params.cfg, agentId: params.agentId, - channel: "bluebubbles", + channel: "localchat", accountId: params.accountId, peer: { kind: isGroup ? "group" : "direct", id: normalizedId }, chatType: isGroup ? "group" : "direct", - from: isGroup ? `group:${rawId}` : `bluebubbles:${rawId}`, - to: `bluebubbles:${stripped}`, + from: isGroup ? `group:${rawId}` : `localchat:${rawId}`, + to: `localchat:${stripped}`, }); } @@ -510,9 +511,9 @@ function resolveTlonOutboundSessionRouteForTest(params: ChannelOutboundSessionRo export function setMinimalOutboundSessionPluginRegistryForTests(): void { const plugins: ChannelPlugin[] = [ createSessionRouteTestPlugin({ - id: "whatsapp", - label: "WhatsApp", - resolveOutboundSessionRoute: resolveWhatsAppOutboundSessionRouteForTest, + id: "mobilechat", + label: "Mobile Chat", + resolveOutboundSessionRoute: resolveMobileChatOutboundSessionRouteForTest, }), createSessionRouteTestPlugin({ id: "matrix", @@ -520,24 +521,24 @@ export function setMinimalOutboundSessionPluginRegistryForTests(): void { resolveOutboundSessionRoute: resolveMatrixOutboundSessionRouteForTest, }), createSessionRouteTestPlugin({ - id: "msteams", - label: "Microsoft Teams", - resolveOutboundSessionRoute: resolveMSTeamsOutboundSessionRouteForTest, + id: "meetingchat", + label: "Meeting Chat", + resolveOutboundSessionRoute: resolveMeetingChatOutboundSessionRouteForTest, }), createSessionRouteTestPlugin({ - id: "slack", - label: "Slack", - resolveOutboundSessionRoute: resolveSlackOutboundSessionRouteForTest, + id: "workspace", + label: "Workspace", + resolveOutboundSessionRoute: resolveWorkspaceOutboundSessionRouteForTest, }), createSessionRouteTestPlugin({ - id: "telegram", - label: "Telegram", - resolveOutboundSessionRoute: resolveTelegramOutboundSessionRouteForTest, + id: "forum", + label: "Forum", + resolveOutboundSessionRoute: resolveForumOutboundSessionRouteForTest, }), createSessionRouteTestPlugin({ - id: "discord", - label: "Discord", - resolveOutboundSessionRoute: resolveDiscordOutboundSessionRouteForTest, + id: "guildchat", + label: "Guild Chat", + resolveOutboundSessionRoute: resolveGuildChatOutboundSessionRouteForTest, }), createSessionRouteTestPlugin({ id: "nextcloud-talk", @@ -545,9 +546,9 @@ export function setMinimalOutboundSessionPluginRegistryForTests(): void { resolveOutboundSessionRoute: resolveNextcloudTalkOutboundSessionRouteForTest, }), createSessionRouteTestPlugin({ - id: "bluebubbles", - label: "BlueBubbles", - resolveOutboundSessionRoute: resolveBlueBubblesOutboundSessionRouteForTest, + id: "localchat", + label: "Local Chat", + resolveOutboundSessionRoute: resolveLocalChatOutboundSessionRouteForTest, }), createSessionRouteTestPlugin({ id: "zalo", @@ -570,14 +571,14 @@ export function setMinimalOutboundSessionPluginRegistryForTests(): void { resolveOutboundSessionRoute: resolveTlonOutboundSessionRouteForTest, }), createSessionRouteTestPlugin({ - id: "feishu", - label: "Feishu", - resolveOutboundSessionRoute: resolveFeishuOutboundSessionRouteForTest, + id: "collabchat", + label: "Collab Chat", + resolveOutboundSessionRoute: resolveCollabChatOutboundSessionRouteForTest, }), createSessionRouteTestPlugin({ - id: "mattermost", - label: "Mattermost", - resolveOutboundSessionRoute: resolveMattermostOutboundSessionRouteForTest, + id: "boardchat", + label: "Board Chat", + resolveOutboundSessionRoute: resolveBoardChatOutboundSessionRouteForTest, }), ]; setActivePluginRegistry( diff --git a/src/infra/outbound/outbound-session.test.ts b/src/infra/outbound/outbound-session.test.ts index afccdf0698a..dc98eaab280 100644 --- a/src/infra/outbound/outbound-session.test.ts +++ b/src/infra/outbound/outbound-session.test.ts @@ -28,13 +28,13 @@ describe("resolveOutboundSessionRoute", () => { session: { dmScope: "per-peer", identityLinks: { - alice: ["discord:123"], + alice: ["guildchat:123"], }, }, } as OpenClawConfig; - const slackMpimCfg = { + const workspaceMpimCfg = { channels: { - slack: { + workspace: { dm: { groupChannels: ["G123"], }, @@ -86,12 +86,12 @@ describe("resolveOutboundSessionRoute", () => { it.each([ { - name: "WhatsApp group jid", + name: "MobileChat group jid", cfg: baseConfig, - channel: "whatsapp", + channel: "mobilechat", target: "120363040000000000@g.us", expected: { - sessionKey: "agent:main:whatsapp:group:120363040000000000@g.us", + sessionKey: "agent:main:mobilechat:group:120363040000000000@g.us", from: "120363040000000000@g.us", to: "120363040000000000@g.us", chatType: "group", @@ -110,75 +110,75 @@ describe("resolveOutboundSessionRoute", () => { }, }, { - name: "MSTeams conversation target", + name: "MeetingChat conversation target", cfg: baseConfig, - channel: "msteams", + channel: "meetingchat", target: "conversation:19:meeting_abc@thread.tacv2", expected: { - sessionKey: "agent:main:msteams:channel:19:meeting_abc@thread.tacv2", - from: "msteams:channel:19:meeting_abc@thread.tacv2", + sessionKey: "agent:main:meetingchat:channel:19:meeting_abc@thread.tacv2", + from: "meetingchat:channel:19:meeting_abc@thread.tacv2", to: "conversation:19:meeting_abc@thread.tacv2", chatType: "channel", }, }, { - name: "Slack thread", + name: "Workspace thread", cfg: baseConfig, - channel: "slack", + channel: "workspace", target: "channel:C123", replyToId: "456", expected: { - sessionKey: "agent:main:slack:channel:c123:thread:456", - from: "slack:channel:C123", + sessionKey: "agent:main:workspace:channel:c123:thread:456", + from: "workspace:channel:C123", to: "channel:C123", threadId: "456", }, }, { - name: "Telegram topic group", + name: "Forum topic group", cfg: baseConfig, - channel: "telegram", + channel: "forum", target: "-100123456:topic:42", expected: { - sessionKey: "agent:main:telegram:group:-100123456:topic:42", - from: "telegram:group:-100123456:topic:42", - to: "telegram:-100123456", + sessionKey: "agent:main:forum:group:-100123456:topic:42", + from: "forum:group:-100123456:topic:42", + to: "forum:-100123456", threadId: 42, }, }, { - name: "Telegram DM with topic", + name: "Forum DM with topic", cfg: perChannelPeerCfg, - channel: "telegram", + channel: "forum", target: "123456789:topic:99", expected: { - sessionKey: "agent:main:telegram:direct:123456789:thread:99", - from: "telegram:123456789:topic:99", - to: "telegram:123456789", + sessionKey: "agent:main:forum:direct:123456789:thread:99", + from: "forum:123456789:topic:99", + to: "forum:123456789", threadId: 99, chatType: "direct", }, }, { - name: "Telegram unresolved username DM", + name: "Forum unresolved username DM", cfg: perChannelPeerCfg, - channel: "telegram", + channel: "forum", target: "@alice", expected: { - sessionKey: "agent:main:telegram:direct:@alice", + sessionKey: "agent:main:forum:direct:@alice", chatType: "direct", }, }, { - name: "Telegram DM scoped threadId fallback", + name: "Forum DM scoped threadId fallback", cfg: perChannelPeerCfg, - channel: "telegram", + channel: "forum", target: "12345", threadId: "12345:99", expected: { - sessionKey: "agent:main:telegram:direct:12345:thread:99", - from: "telegram:12345:topic:99", - to: "telegram:12345", + sessionKey: "agent:main:forum:direct:12345:thread:99", + from: "forum:12345:topic:99", + to: "forum:12345", threadId: 99, chatType: "direct", }, @@ -186,7 +186,7 @@ describe("resolveOutboundSessionRoute", () => { { name: "identity-links per-peer", cfg: identityLinksCfg, - channel: "discord", + channel: "guildchat", target: "user:123", expected: { sessionKey: "agent:main:direct:alice", @@ -205,12 +205,12 @@ describe("resolveOutboundSessionRoute", () => { }, }, { - name: "BlueBubbles chat_* prefix stripping", + name: "LocalChat chat_* prefix stripping", cfg: baseConfig, - channel: "bluebubbles", + channel: "localchat", target: "chat_guid:ABC123", expected: { - sessionKey: "agent:main:bluebubbles:group:abc123", + sessionKey: "agent:main:localchat:group:abc123", from: "group:ABC123", }, }, @@ -261,71 +261,71 @@ describe("resolveOutboundSessionRoute", () => { }, }, { - name: "Slack mpim allowlist -> group key", - cfg: slackMpimCfg, - channel: "slack", + name: "Workspace group allowlist -> group key", + cfg: workspaceMpimCfg, + channel: "workspace", target: "channel:G123", expected: { - sessionKey: "agent:main:slack:group:g123", - from: "slack:group:G123", + sessionKey: "agent:main:workspace:group:g123", + from: "workspace:group:G123", }, }, { - name: "Feishu explicit group prefix keeps group routing", + name: "CollabChat explicit group prefix keeps group routing", cfg: baseConfig, - channel: "feishu", + channel: "collabchat", target: "group:oc_group_chat", expected: { - sessionKey: "agent:main:feishu:group:oc_group_chat", - from: "feishu:group:oc_group_chat", + sessionKey: "agent:main:collabchat:group:oc_group_chat", + from: "collabchat:group:oc_group_chat", to: "oc_group_chat", chatType: "group", }, }, { - name: "Feishu explicit dm prefix keeps direct routing", + name: "CollabChat explicit dm prefix keeps direct routing", cfg: perChannelPeerCfg, - channel: "feishu", + channel: "collabchat", target: "dm:oc_dm_chat", expected: { - sessionKey: "agent:main:feishu:direct:oc_dm_chat", - from: "feishu:oc_dm_chat", + sessionKey: "agent:main:collabchat:direct:oc_dm_chat", + from: "collabchat:oc_dm_chat", to: "oc_dm_chat", chatType: "direct", }, }, { - name: "Feishu bare oc_ target defaults to direct routing", + name: "CollabChat bare oc_ target defaults to direct routing", cfg: perChannelPeerCfg, - channel: "feishu", + channel: "collabchat", target: "oc_ambiguous_chat", expected: { - sessionKey: "agent:main:feishu:direct:oc_ambiguous_chat", - from: "feishu:oc_ambiguous_chat", + sessionKey: "agent:main:collabchat:direct:oc_ambiguous_chat", + from: "collabchat:oc_ambiguous_chat", to: "oc_ambiguous_chat", chatType: "direct", }, }, { - name: "Slack user DM target", + name: "Workspace user DM target", cfg: perChannelPeerCfg, - channel: "slack", + channel: "workspace", target: "user:U12345ABC", expected: { - sessionKey: "agent:main:slack:direct:u12345abc", - from: "slack:U12345ABC", + sessionKey: "agent:main:workspace:direct:u12345abc", + from: "workspace:U12345ABC", to: "user:U12345ABC", chatType: "direct", }, }, { - name: "Slack channel target without thread", + name: "Workspace channel target without thread", cfg: baseConfig, - channel: "slack", + channel: "workspace", target: "channel:C999XYZ", expected: { - sessionKey: "agent:main:slack:channel:c999xyz", - from: "slack:channel:C999XYZ", + sessionKey: "agent:main:workspace:channel:c999xyz", + from: "workspace:channel:C999XYZ", to: "channel:C999XYZ", chatType: "channel", }, @@ -336,7 +336,7 @@ describe("resolveOutboundSessionRoute", () => { it.each([ { - name: "uses resolved Discord user targets to route bare numeric ids as DMs", + name: "uses resolved GuildChat user targets to route bare numeric ids as DMs", target: "123", resolvedTarget: { to: "user:123", @@ -344,14 +344,14 @@ describe("resolveOutboundSessionRoute", () => { source: "directory" as const, }, expected: { - sessionKey: "agent:main:discord:direct:123", - from: "discord:123", + sessionKey: "agent:main:guildchat:direct:123", + from: "guildchat:123", to: "user:123", chatType: "direct", }, }, { - name: "uses resolved Discord channel targets to route bare numeric ids as channels without thread suffixes", + name: "uses resolved GuildChat channel targets to route bare numeric ids as channels without thread suffixes", target: "456", threadId: "789", resolvedTarget: { @@ -360,31 +360,31 @@ describe("resolveOutboundSessionRoute", () => { source: "directory" as const, }, expected: { - sessionKey: "agent:main:discord:channel:456", - baseSessionKey: "agent:main:discord:channel:456", - from: "discord:channel:456", + sessionKey: "agent:main:guildchat:channel:456", + baseSessionKey: "agent:main:guildchat:channel:456", + from: "guildchat:channel:456", to: "channel:456", chatType: "channel", threadId: "789", }, }, { - name: "uses resolved Mattermost user targets to route bare ids as DMs", + name: "uses resolved BoardChat user targets to route bare ids as DMs", target: "dthcxgoxhifn3pwh65cut3ud3w", - channel: "mattermost", + channel: "boardchat", resolvedTarget: { to: "user:dthcxgoxhifn3pwh65cut3ud3w", kind: "user" as const, source: "directory" as const, }, expected: { - sessionKey: "agent:main:mattermost:direct:dthcxgoxhifn3pwh65cut3ud3w", - from: "mattermost:dthcxgoxhifn3pwh65cut3ud3w", + sessionKey: "agent:main:boardchat:direct:dthcxgoxhifn3pwh65cut3ud3w", + from: "boardchat:dthcxgoxhifn3pwh65cut3ud3w", to: "user:dthcxgoxhifn3pwh65cut3ud3w", chatType: "direct", }, }, - ])("$name", async ({ channel = "discord", target, threadId, resolvedTarget, expected }) => { + ])("$name", async ({ channel = "guildchat", target, threadId, resolvedTarget, expected }) => { const route = await resolveOutboundSessionRoute({ cfg: perChannelPeerSessionCfg, channel, @@ -397,15 +397,15 @@ describe("resolveOutboundSessionRoute", () => { expect(route).toMatchObject(expected); }); - it("rejects bare numeric Discord targets when the caller has no kind hint", async () => { + it("rejects bare numeric GuildChat targets when the caller has no kind hint", async () => { await expect( resolveOutboundSessionRoute({ cfg: perChannelPeerSessionCfg, - channel: "discord", + channel: "guildchat", agentId: "main", target: "123", }), - ).rejects.toThrow(/Ambiguous Discord recipient/); + ).rejects.toThrow(/Ambiguous Guild Chat recipient/); }); }); @@ -422,13 +422,13 @@ describe("ensureOutboundSessionEntry", () => { store: "/stores/{agentId}.json", }, } as OpenClawConfig, - channel: "slack", + channel: "workspace", route: { - sessionKey: "agent:main:slack:channel:c1", - baseSessionKey: "agent:work:slack:channel:resolved", + sessionKey: "agent:main:workspace:channel:c1", + baseSessionKey: "agent:work:workspace:channel:resolved", peer: { kind: "channel", id: "c1" }, chatType: "channel", - from: "slack:channel:C1", + from: "workspace:channel:C1", to: "channel:C1", }, }); @@ -439,7 +439,7 @@ describe("ensureOutboundSessionEntry", () => { expect(mocks.recordSessionMetaFromInbound).toHaveBeenCalledWith( expect.objectContaining({ storePath: "/stores/main.json", - sessionKey: "agent:main:slack:channel:c1", + sessionKey: "agent:main:workspace:channel:c1", }), ); }); diff --git a/src/infra/outbound/target-resolver.test.ts b/src/infra/outbound/target-resolver.test.ts index ce69205a565..ba34e0f3402 100644 --- a/src/infra/outbound/target-resolver.test.ts +++ b/src/infra/outbound/target-resolver.test.ts @@ -135,7 +135,7 @@ describe("resolveMessagingTarget (directory fallback)", () => { const result = await expectOkResolution({ cfg, - channel: "mattermost", + channel: "workspace", input: "dthcxgoxhifn3pwh65cut3ud3w", }); expect(result.target).toEqual({