diff --git a/src/infra/outbound/message-action-normalization.test.ts b/src/infra/outbound/message-action-normalization.test.ts index 6ea00a526c2..39a5353accc 100644 --- a/src/infra/outbound/message-action-normalization.test.ts +++ b/src/infra/outbound/message-action-normalization.test.ts @@ -4,7 +4,13 @@ import { normalizeMessageActionInput } from "./message-action-normalization.js"; vi.mock("../../channels/plugins/bootstrap-registry.js", async () => ({ getBootstrapChannelPlugin: ( await import("./message-action-test-fixtures.js") - ).createFeishuMessageActionBootstrapRegistryMock(), + ).createPinboardMessageActionBootstrapRegistryMock(), +})); + +vi.mock("../../utils/message-channel.js", () => ({ + isDeliverableMessageChannel: (value: string) => ["workspace", "forum"].includes(value), + normalizeMessageChannel: (value?: string | null) => + typeof value === "string" ? value.trim().toLowerCase() : undefined, })); describe("normalizeMessageActionInput", () => { @@ -66,10 +72,10 @@ describe("normalizeMessageActionInput", () => { }, toolContext: { currentChannelId: "C1", - currentChannelProvider: "slack", + currentChannelProvider: "workspace", }, }, - expectedFields: { channel: "slack" }, + expectedFields: { channel: "workspace" }, }, { input: { @@ -110,7 +116,7 @@ describe("normalizeMessageActionInput", () => { input: { action: "pin", args: { - channel: "feishu", + channel: "pinboard", messageId: "om_123", }, }, @@ -121,7 +127,7 @@ describe("normalizeMessageActionInput", () => { input: { action: "list-pins", args: { - channel: "feishu", + channel: "pinboard", chatId: "oc_123", }, }, @@ -132,12 +138,12 @@ describe("normalizeMessageActionInput", () => { input: { action: "read", args: { - channel: "slack", + channel: "workspace", messageId: "123.456", }, toolContext: { currentChannelId: "C12345678", - currentChannelProvider: "slack", + currentChannelProvider: "workspace", }, }, expectedFields: { target: "C12345678", messageId: "123.456" }, diff --git a/src/infra/outbound/message-action-params.test.ts b/src/infra/outbound/message-action-params.test.ts index 9bda17441dd..ffe22b6b9ef 100644 --- a/src/infra/outbound/message-action-params.test.ts +++ b/src/infra/outbound/message-action-params.test.ts @@ -35,9 +35,9 @@ describe("message action media helpers", () => { resolveExtraActionMediaSourceParamKeys({ cfg, action: "send", - channel: "slack", + channel: "workspace", args: { - channel: "slack", + channel: "workspace", target: "#C12345678", message: "hi", media: "https://example.com/photo.png", @@ -157,7 +157,7 @@ describe("message action media helpers", () => { } }); - maybeIt("normalizes Discord event image sandbox media params", async () => { + maybeIt("normalizes extension event image sandbox media params", async () => { const sandboxRoot = await fs.mkdtemp(path.join(os.tmpdir(), "msg-params-image-")); try { const args: Record = { @@ -180,7 +180,7 @@ describe("message action media helpers", () => { } }); - maybeIt("normalizes Matrix avatarPath and avatarUrl sandbox media params", async () => { + maybeIt("normalizes extension avatarPath and avatarUrl sandbox media params", async () => { const sandboxRoot = await fs.mkdtemp(path.join(os.tmpdir(), "msg-params-avatar-")); try { const args: Record = { @@ -227,7 +227,7 @@ describe("message action media helpers", () => { ]); }); - maybeIt("normalizes Matrix snake_case avatar_path and avatar_url aliases", async () => { + maybeIt("normalizes extension snake_case avatar_path and avatar_url aliases", async () => { const sandboxRoot = await fs.mkdtemp(path.join(os.tmpdir(), "msg-params-avatar-snake-")); try { const args: Record = { @@ -253,7 +253,7 @@ describe("message action media helpers", () => { } }); - maybeIt("prefers canonical Matrix media params over invalid snake_case aliases", async () => { + maybeIt("prefers canonical extension media params over invalid snake_case aliases", async () => { const sandboxRoot = await fs.mkdtemp(path.join(os.tmpdir(), "msg-params-avatar-canonical-")); try { const args: Record = { @@ -369,7 +369,7 @@ describe("message action media helpers", () => { }; await hydrateAttachmentParamsForAction({ cfg, - channel: "slack", + channel: "workspace", args: mediaArgs, action: "sendAttachment", dryRun: true, @@ -382,7 +382,7 @@ describe("message action media helpers", () => { }; await hydrateAttachmentParamsForAction({ cfg, - channel: "slack", + channel: "workspace", args: fileArgs, action: "sendAttachment", dryRun: true, @@ -398,7 +398,7 @@ describe("message action media helpers", () => { await hydrateAttachmentParamsForAction({ cfg, - channel: "slack", + channel: "workspace", args, action: "sendAttachment", dryRun: true, @@ -441,7 +441,7 @@ describe("message action sandbox media hydration", () => { await expect( hydrateAttachmentParamsForAction({ cfg, - channel: "slack", + channel: "workspace", args, action: "sendAttachment", mediaPolicy, diff --git a/src/infra/outbound/message-action-runner.context.test.ts b/src/infra/outbound/message-action-runner.context.test.ts index 2cf9ab00813..41f9a985eec 100644 --- a/src/infra/outbound/message-action-runner.context.test.ts +++ b/src/infra/outbound/message-action-runner.context.test.ts @@ -7,30 +7,30 @@ import { createTestRegistry, } from "../../test-utils/channel-plugins.js"; import { + directChatConfig, + directChatTestPlugin, directOutbound, + forumTestPlugin, runDryAction, runDrySend, - slackConfig, - slackTestPlugin, - telegramTestPlugin, - whatsappConfig, - whatsappTestPlugin, + workspaceConfig, + workspaceTestPlugin, } from "./message-action-runner.test-helpers.js"; -const imessageTestPlugin: ChannelPlugin = { +const localChatTestPlugin: ChannelPlugin = { ...createChannelTestPluginBase({ - id: "imessage", - label: "iMessage", - docsPath: "/channels/imessage", + id: "localchat", + label: "Local Chat", + docsPath: "/channels/localchat", capabilities: { chatTypes: ["direct", "group"], media: true }, }), meta: { - id: "imessage", - label: "iMessage", - selectionLabel: "iMessage (imsg)", - docsPath: "/channels/imessage", - blurb: "iMessage test stub.", - aliases: ["imsg"], + id: "localchat", + label: "Local Chat", + selectionLabel: "Local Chat (local)", + docsPath: "/channels/localchat", + blurb: "Local chat test stub.", + aliases: ["local"], }, outbound: directOutbound, messaging: { @@ -47,24 +47,24 @@ describe("runMessageAction context isolation", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "slack", + pluginId: "workspace", source: "test", - plugin: slackTestPlugin, + plugin: workspaceTestPlugin, }, { - pluginId: "whatsapp", + pluginId: "directchat", source: "test", - plugin: whatsappTestPlugin, + plugin: directChatTestPlugin, }, { - pluginId: "telegram", + pluginId: "forum", source: "test", - plugin: telegramTestPlugin, + plugin: forumTestPlugin, }, { - pluginId: "imessage", + pluginId: "localchat", source: "test", - plugin: imessageTestPlugin, + plugin: localChatTestPlugin, }, ]), ); @@ -77,9 +77,9 @@ describe("runMessageAction context isolation", () => { it.each([ { name: "allows send when target matches current channel", - cfg: slackConfig, + cfg: workspaceConfig, actionParams: { - channel: "slack", + channel: "workspace", target: "#C12345678", message: "hi", }, @@ -87,27 +87,27 @@ describe("runMessageAction context isolation", () => { }, { name: "accepts legacy to parameter for send", - cfg: slackConfig, + cfg: workspaceConfig, actionParams: { - channel: "slack", + channel: "workspace", to: "#C12345678", message: "hi", }, }, { name: "defaults to current channel when target is omitted", - cfg: slackConfig, + cfg: workspaceConfig, actionParams: { - channel: "slack", + channel: "workspace", message: "hi", }, toolContext: { currentChannelId: "C12345678" }, }, { name: "allows media-only send when target matches current channel", - cfg: slackConfig, + cfg: workspaceConfig, actionParams: { - channel: "slack", + channel: "workspace", target: "#C12345678", media: "https://example.com/note.ogg", }, @@ -115,9 +115,9 @@ describe("runMessageAction context isolation", () => { }, { name: "allows send when poll booleans are explicitly false", - cfg: slackConfig, + cfg: workspaceConfig, actionParams: { - channel: "slack", + channel: "workspace", target: "#C12345678", message: "hi", pollMulti: false, @@ -138,31 +138,31 @@ describe("runMessageAction context isolation", () => { it.each([ { - name: "send when target differs from current slack channel", + name: "send when target differs from current workspace channel", run: () => runDrySend({ - cfg: slackConfig, + cfg: workspaceConfig, actionParams: { - channel: "slack", + channel: "workspace", target: "channel:C99999999", message: "hi", }, - toolContext: { currentChannelId: "C12345678", currentChannelProvider: "slack" }, + toolContext: { currentChannelId: "C12345678", currentChannelProvider: "workspace" }, }), expectedKind: "send", }, { - name: "thread-reply when channelId differs from current slack channel", + name: "thread-reply when channelId differs from current workspace channel", run: () => runDryAction({ - cfg: slackConfig, + cfg: workspaceConfig, action: "thread-reply", actionParams: { - channel: "slack", + channel: "workspace", target: "C99999999", message: "hi", }, - toolContext: { currentChannelId: "C12345678", currentChannelProvider: "slack" }, + toolContext: { currentChannelId: "C12345678", currentChannelProvider: "workspace" }, }), expectedKind: "action", }, @@ -173,34 +173,34 @@ describe("runMessageAction context isolation", () => { it.each([ { - name: "whatsapp match", - channel: "whatsapp", + name: "direct chat match", + channel: "directchat", target: "123@g.us", currentChannelId: "123@g.us", }, { - name: "imessage match", - channel: "imessage", - target: "imessage:+15551234567", - currentChannelId: "imessage:+15551234567", + name: "local chat match", + channel: "localchat", + target: "localchat:+15551234567", + currentChannelId: "localchat:+15551234567", }, { - name: "whatsapp mismatch", - channel: "whatsapp", + name: "direct chat mismatch", + channel: "directchat", target: "456@g.us", currentChannelId: "123@g.us", - currentChannelProvider: "whatsapp", + currentChannelProvider: "directchat", }, { - name: "imessage mismatch", - channel: "imessage", - target: "imessage:+15551230000", - currentChannelId: "imessage:+15551234567", - currentChannelProvider: "imessage", + name: "local chat mismatch", + channel: "localchat", + target: "localchat:+15551230000", + currentChannelId: "localchat:+15551234567", + currentChannelProvider: "localchat", }, ] as const)("$name", async (testCase) => { const result = await runDrySend({ - cfg: whatsappConfig, + cfg: directChatConfig, actionParams: { channel: testCase.channel, target: testCase.target, @@ -222,12 +222,12 @@ describe("runMessageAction context isolation", () => { name: "infers channel + target from tool context when missing", cfg: { channels: { - slack: { - botToken: "xoxb-test", - appToken: "xapp-test", + workspace: { + botToken: "workspace-test", + appToken: "workspace-app-test", }, - telegram: { - token: "tg-test", + forum: { + token: "forum-test", }, }, } as OpenClawConfig, @@ -235,35 +235,35 @@ describe("runMessageAction context isolation", () => { actionParams: { message: "hi", }, - toolContext: { currentChannelId: "C12345678", currentChannelProvider: "slack" }, + toolContext: { currentChannelId: "C12345678", currentChannelProvider: "workspace" }, expectedKind: "send", - expectedChannel: "slack", + expectedChannel: "workspace", }, { name: "falls back to tool-context provider when channel param is an id", - cfg: slackConfig, + cfg: workspaceConfig, action: "send" as const, actionParams: { channel: "C12345678", target: "#C12345678", message: "hi", }, - toolContext: { currentChannelId: "C12345678", currentChannelProvider: "slack" }, + toolContext: { currentChannelId: "C12345678", currentChannelProvider: "workspace" }, expectedKind: "send", - expectedChannel: "slack", + expectedChannel: "workspace", }, { name: "falls back to tool-context provider for broadcast channel ids", - cfg: slackConfig, + cfg: workspaceConfig, action: "broadcast" as const, actionParams: { targets: ["channel:C12345678"], channel: "C12345678", message: "hi", }, - toolContext: { currentChannelProvider: "slack" }, + toolContext: { currentChannelProvider: "workspace" }, expectedKind: "broadcast", - expectedChannel: "slack", + expectedChannel: "workspace", }, ])("$name", async ({ cfg, action, actionParams, toolContext, expectedKind, expectedChannel }) => { const result = await runDryAction({ @@ -281,20 +281,20 @@ describe("runMessageAction context isolation", () => { { name: "blocks cross-provider sends by default", action: "send" as const, - cfg: slackConfig, + cfg: workspaceConfig, actionParams: { - channel: "telegram", + channel: "forum", target: "@opsbot", message: "hi", }, - toolContext: { currentChannelId: "C12345678", currentChannelProvider: "slack" }, + toolContext: { currentChannelId: "C12345678", currentChannelProvider: "workspace" }, message: /Cross-context messaging denied/, }, { name: "blocks same-provider cross-context when disabled", action: "send" as const, cfg: { - ...slackConfig, + ...workspaceConfig, tools: { message: { crossContext: { @@ -304,18 +304,18 @@ describe("runMessageAction context isolation", () => { }, } as OpenClawConfig, actionParams: { - channel: "slack", + channel: "workspace", target: "channel:C99999999", message: "hi", }, - toolContext: { currentChannelId: "C12345678", currentChannelProvider: "slack" }, + toolContext: { currentChannelId: "C12345678", currentChannelProvider: "workspace" }, message: /Cross-context messaging denied/, }, { name: "blocks same-provider cross-context uploads when disabled", action: "upload-file" as const, cfg: { - ...slackConfig, + ...workspaceConfig, tools: { message: { crossContext: { @@ -325,19 +325,19 @@ describe("runMessageAction context isolation", () => { }, } as OpenClawConfig, actionParams: { - channel: "slack", + channel: "workspace", target: "channel:C99999999", filePath: "/tmp/report.png", }, - toolContext: { currentChannelId: "C12345678", currentChannelProvider: "slack" }, + toolContext: { currentChannelId: "C12345678", currentChannelProvider: "workspace" }, message: /Cross-context messaging denied/, }, { name: "rejects channel ids that resolve to user targets", action: "channel-info" as const, - cfg: slackConfig, + cfg: workspaceConfig, actionParams: { - channel: "slack", + channel: "workspace", channelId: "U12345678", }, message: 'Channel id "U12345678" resolved to a user target.', @@ -358,9 +358,9 @@ describe("runMessageAction context isolation", () => { name: "send", run: (abortSignal: AbortSignal) => runDrySend({ - cfg: slackConfig, + cfg: workspaceConfig, actionParams: { - channel: "slack", + channel: "workspace", target: "#C12345678", message: "hi", }, @@ -371,11 +371,11 @@ describe("runMessageAction context isolation", () => { name: "broadcast", run: (abortSignal: AbortSignal) => runDryAction({ - cfg: slackConfig, + cfg: workspaceConfig, action: "broadcast", actionParams: { targets: ["channel:C12345678"], - channel: "slack", + channel: "workspace", message: "hi", }, abortSignal, diff --git a/src/infra/outbound/message-action-runner.send-validation.test.ts b/src/infra/outbound/message-action-runner.send-validation.test.ts index e16dc2579c3..2cc2f7cb23c 100644 --- a/src/infra/outbound/message-action-runner.send-validation.test.ts +++ b/src/infra/outbound/message-action-runner.send-validation.test.ts @@ -3,10 +3,10 @@ import type { OpenClawConfig } from "../../config/config.js"; import { setActivePluginRegistry } from "../../plugins/runtime.js"; import { createTestRegistry } from "../../test-utils/channel-plugins.js"; import { + forumTestPlugin, runDrySend, - slackConfig, - slackTestPlugin, - telegramTestPlugin, + workspaceConfig, + workspaceTestPlugin, } from "./message-action-runner.test-helpers.js"; describe("runMessageAction send validation", () => { @@ -14,14 +14,14 @@ describe("runMessageAction send validation", () => { setActivePluginRegistry( createTestRegistry([ { - pluginId: "slack", + pluginId: "workspace", source: "test", - plugin: slackTestPlugin, + plugin: workspaceTestPlugin, }, { - pluginId: "telegram", + pluginId: "forum", source: "test", - plugin: telegramTestPlugin, + plugin: forumTestPlugin, }, ]), ); @@ -34,9 +34,9 @@ describe("runMessageAction send validation", () => { it("requires message when no media hint is provided", async () => { await expect( runDrySend({ - cfg: slackConfig, + cfg: workspaceConfig, actionParams: { - channel: "slack", + channel: "workspace", target: "#C12345678", }, toolContext: { currentChannelId: "C12345678" }, @@ -48,13 +48,13 @@ describe("runMessageAction send validation", () => { const result = await runDrySend({ cfg: { channels: { - telegram: { - botToken: "telegram-test", + forum: { + botToken: "forum-test", }, }, } as OpenClawConfig, actionParams: { - channel: "telegram", + channel: "forum", target: "123456", interactive: { blocks: [ @@ -70,11 +70,11 @@ describe("runMessageAction send validation", () => { expect(result.kind).toBe("send"); }); - it("allows send when only Slack blocks are provided", async () => { + it("allows send when only channel-specific blocks are provided", async () => { const result = await runDrySend({ - cfg: slackConfig, + cfg: workspaceConfig, actionParams: { - channel: "slack", + channel: "workspace", target: "#C12345678", blocks: [{ type: "divider" }], }, @@ -88,7 +88,7 @@ describe("runMessageAction send validation", () => { { name: "structured poll params", actionParams: { - channel: "slack", + channel: "workspace", target: "#C12345678", message: "hi", pollQuestion: "Ready?", @@ -98,7 +98,7 @@ describe("runMessageAction send validation", () => { { name: "string-encoded poll params", actionParams: { - channel: "slack", + channel: "workspace", target: "#C12345678", message: "hi", pollDurationSeconds: "60", @@ -108,7 +108,7 @@ describe("runMessageAction send validation", () => { { name: "snake_case poll params", actionParams: { - channel: "slack", + channel: "workspace", target: "#C12345678", message: "hi", poll_question: "Ready?", @@ -119,7 +119,7 @@ describe("runMessageAction send validation", () => { { name: "negative poll duration params", actionParams: { - channel: "slack", + channel: "workspace", target: "#C12345678", message: "hi", pollDurationSeconds: -5, @@ -128,7 +128,7 @@ describe("runMessageAction send validation", () => { ])("rejects send actions that include $name", async ({ actionParams }) => { await expect( runDrySend({ - cfg: slackConfig, + cfg: workspaceConfig, actionParams, toolContext: { currentChannelId: "C12345678" }, }), diff --git a/src/infra/outbound/message-action-runner.test-helpers.ts b/src/infra/outbound/message-action-runner.test-helpers.ts index 1489d098ceb..99d59dfda28 100644 --- a/src/infra/outbound/message-action-runner.test-helpers.ts +++ b/src/infra/outbound/message-action-runner.test-helpers.ts @@ -9,18 +9,18 @@ import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { createChannelTestPluginBase } from "../../test-utils/channel-plugins.js"; import { runMessageAction } from "./message-action-runner.js"; -export const slackConfig = { +export const workspaceConfig = { channels: { - slack: { - botToken: "xoxb-test", - appToken: "xapp-test", + workspace: { + botToken: "workspace-test", + appToken: "workspace-app-test", }, }, } as OpenClawConfig; -export const whatsappConfig = { +export const directChatConfig = { channels: { - whatsapp: { + directchat: { allowFrom: ["*"], }, }, @@ -60,7 +60,7 @@ export const runDrySend = (params: { type ResolvedTestTarget = { to: string; kind: ChannelDirectoryEntryKind }; -export function normalizeSlackTarget(raw: string): string { +export function normalizeWorkspaceTarget(raw: string): string { const trimmed = raw.trim(); if (!trimmed) { return trimmed; @@ -82,7 +82,7 @@ export function normalizeSlackTarget(raw: string): string { } export function createConfiguredTestPlugin(params: { - id: "slack" | "telegram" | "whatsapp"; + id: string; isConfigured: (cfg: OpenClawConfig) => boolean; normalizeTarget: (raw: string) => string | undefined; resolveTarget: (input: string) => ResolvedTestTarget | null; @@ -114,12 +114,12 @@ export function createConfiguredTestPlugin(params: { }; } -export const slackTestPlugin = createConfiguredTestPlugin({ - id: "slack", - isConfigured: (cfg) => Boolean(cfg.channels?.slack?.botToken?.trim()), - normalizeTarget: (raw) => normalizeSlackTarget(raw) || undefined, +export const workspaceTestPlugin = createConfiguredTestPlugin({ + id: "workspace", + isConfigured: (cfg) => Boolean(cfg.channels?.workspace?.botToken?.trim()), + normalizeTarget: (raw) => normalizeWorkspaceTarget(raw) || undefined, resolveTarget: (input) => { - const normalized = normalizeSlackTarget(input); + const normalized = normalizeWorkspaceTarget(input); if (!normalized) { return null; } @@ -131,9 +131,9 @@ export const slackTestPlugin = createConfiguredTestPlugin({ }, }); -export const telegramTestPlugin = createConfiguredTestPlugin({ - id: "telegram", - isConfigured: (cfg) => Boolean(cfg.channels?.telegram?.botToken?.trim()), +export const forumTestPlugin = createConfiguredTestPlugin({ + id: "forum", + isConfigured: (cfg) => Boolean(cfg.channels?.forum?.botToken?.trim()), normalizeTarget: (raw) => raw.trim() || undefined, resolveTarget: (input) => { const normalized = input.trim(); @@ -141,15 +141,15 @@ export const telegramTestPlugin = createConfiguredTestPlugin({ return null; } return { - to: normalized.replace(/^telegram:/i, ""), + to: normalized.replace(/^forum:/i, ""), kind: normalized.startsWith("@") ? "user" : "group", }; }, }); -export const whatsappTestPlugin = createConfiguredTestPlugin({ - id: "whatsapp", - isConfigured: (cfg) => Boolean(cfg.channels?.whatsapp), +export const directChatTestPlugin = createConfiguredTestPlugin({ + id: "directchat", + isConfigured: (cfg) => Boolean(cfg.channels?.directchat), normalizeTarget: (raw) => raw.trim() || undefined, resolveTarget: (input) => { const normalized = input.trim(); diff --git a/src/infra/outbound/message-action-spec.test.ts b/src/infra/outbound/message-action-spec.test.ts index a2b9cf1f7c3..5d2ff8e9886 100644 --- a/src/infra/outbound/message-action-spec.test.ts +++ b/src/infra/outbound/message-action-spec.test.ts @@ -4,7 +4,7 @@ import { actionHasTarget, actionRequiresTarget } from "./message-action-spec.js" vi.mock("../../channels/plugins/bootstrap-registry.js", async () => ({ getBootstrapChannelPlugin: ( await import("./message-action-test-fixtures.js") - ).createFeishuMessageActionBootstrapRegistryMock(), + ).createPinboardMessageActionBootstrapRegistryMock(), })); describe("actionRequiresTarget", () => { @@ -26,32 +26,32 @@ describe("actionHasTarget", () => { { action: "read", params: { messageId: "msg_123" }, - ctx: { channel: "feishu" }, + ctx: { channel: "pinboard" }, expected: true, }, { action: "edit", params: { messageId: " msg_123 " }, expected: true }, { action: "pin", params: { messageId: "msg_123" }, - ctx: { channel: "feishu" }, + ctx: { channel: "pinboard" }, expected: true, }, { action: "unpin", params: { messageId: "msg_123" }, - ctx: { channel: "feishu" }, + ctx: { channel: "pinboard" }, expected: true, }, { action: "list-pins", params: { chatId: "oc_123" }, - ctx: { channel: "feishu" }, + ctx: { channel: "pinboard" }, expected: true, }, { action: "channel-info", params: { chatId: "oc_123" }, - ctx: { channel: "feishu" }, + ctx: { channel: "pinboard" }, expected: true, }, { action: "react", params: { chatGuid: "chat-guid" }, expected: true }, @@ -61,13 +61,13 @@ describe("actionHasTarget", () => { { action: "pin", params: { messageId: "msg_123" }, - ctx: { channel: "slack" }, + ctx: { channel: "workspace" }, expected: false, }, { action: "channel-info", params: { chatId: "oc_123" }, - ctx: { channel: "discord" }, + ctx: { channel: "richchat" }, expected: false, }, { action: "edit", params: { messageId: " " }, expected: false }, diff --git a/src/infra/outbound/message-action-test-fixtures.ts b/src/infra/outbound/message-action-test-fixtures.ts index 184e902ee80..7cbefe92811 100644 --- a/src/infra/outbound/message-action-test-fixtures.ts +++ b/src/infra/outbound/message-action-test-fixtures.ts @@ -1,6 +1,6 @@ -export function createFeishuMessageActionBootstrapRegistryMock() { +export function createPinboardMessageActionBootstrapRegistryMock() { return (channel: string) => - channel === "feishu" + channel === "pinboard" ? { actions: { messageActionTargetAliases: { diff --git a/src/infra/outbound/outbound-policy.test.ts b/src/infra/outbound/outbound-policy.test.ts index 7bc69b405c3..28a2d6c0847 100644 --- a/src/infra/outbound/outbound-policy.test.ts +++ b/src/infra/outbound/outbound-policy.test.ts @@ -16,13 +16,13 @@ class TestSeparator { constructor(readonly options: { divider: boolean; spacing: string }) {} } -class TestDiscordUiContainer { +class TestRichUiContainer { constructor(readonly components: Array) {} } const mocks = vi.hoisted(() => ({ getChannelMessageAdapter: vi.fn((channel: string) => - channel === "discord" + channel === "richchat" ? { supportsComponentsV2: true, buildCrossContextComponents: ({ @@ -39,7 +39,7 @@ const mocks = vi.hoisted(() => ({ components.push(new TestSeparator({ divider: true, spacing: "small" })); } components.push(new TestTextDisplay(`*From ${originLabel}*`)); - return [new TestDiscordUiContainer(components)]; + return [new TestRichUiContainer(components)]; }, } : { supportsComponentsV2: false }, @@ -49,7 +49,7 @@ const mocks = vi.hoisted(() => ({ if (!trimmed) { return undefined; } - if (channel === "slack") { + if (channel === "workspace") { return trimmed.replace(/^#/, ""); } return trimmed; @@ -75,18 +75,18 @@ vi.mock("./target-resolver.js", () => ({ lookupDirectoryDisplay: mocks.lookupDirectoryDisplay, })); -const slackConfig = { +const workspaceConfig = { channels: { - slack: { - botToken: "xoxb-test", - appToken: "xapp-test", + workspace: { + botToken: "workspace-test", + appToken: "workspace-app-test", }, }, } as OpenClawConfig; -const discordConfig = { +const richChatConfig = { channels: { - discord: {}, + richchat: {}, }, } as OpenClawConfig; @@ -134,53 +134,53 @@ describe("outbound policy helpers", () => { it.each([ { cfg: { - ...slackConfig, + ...workspaceConfig, tools: { message: { crossContext: { allowAcrossProviders: true } }, }, } as OpenClawConfig, - channel: "telegram", + channel: "forum", action: "send" as const, - to: "telegram:@ops", + to: "forum:@ops", currentChannelId: "C12345678", - currentChannelProvider: "slack", + currentChannelProvider: "workspace", expected: "allow" as const, }, { - cfg: slackConfig, - channel: "telegram", + cfg: workspaceConfig, + channel: "forum", action: "send" as const, - to: "telegram:@ops", + to: "forum:@ops", currentChannelId: "C12345678", - currentChannelProvider: "slack", - expected: /target provider "telegram" while bound to "slack"/, + currentChannelProvider: "workspace", + expected: /target provider "forum" while bound to "workspace"/, }, { cfg: { - ...slackConfig, + ...workspaceConfig, tools: { message: { crossContext: { allowWithinProvider: false } }, }, } as OpenClawConfig, - channel: "slack", + channel: "workspace", action: "send" as const, to: "C999", currentChannelId: "C123", - currentChannelProvider: "slack", + currentChannelProvider: "workspace", expected: /target="C999" while bound to "C123"/, }, { cfg: { - ...slackConfig, + ...workspaceConfig, tools: { message: { crossContext: { allowWithinProvider: false } }, }, } as OpenClawConfig, - channel: "slack", + channel: "workspace", action: "upload-file" as const, to: "C999", currentChannelId: "C123", - currentChannelProvider: "slack", + currentChannelProvider: "workspace", expected: /target="C999" while bound to "C123"/, }, ])("enforces cross-context policy for %j", (params) => { @@ -189,10 +189,10 @@ describe("outbound policy helpers", () => { it("uses components when available and preferred", async () => { const decoration = await buildCrossContextDecoration({ - cfg: discordConfig, - channel: "discord", + cfg: richChatConfig, + channel: "richchat", target: "123", - toolContext: { currentChannelId: "C12345678", currentChannelProvider: "discord" }, + toolContext: { currentChannelId: "C12345678", currentChannelProvider: "richchat" }, }); expect(decoration).not.toBeNull(); @@ -211,12 +211,12 @@ describe("outbound policy helpers", () => { it("returns null when decoration is skipped and falls back to text markers", async () => { await expect( buildCrossContextDecoration({ - cfg: discordConfig, - channel: "discord", + cfg: richChatConfig, + channel: "richchat", target: "123", toolContext: { currentChannelId: "C12345678", - currentChannelProvider: "discord", + currentChannelProvider: "richchat", skipCrossContextDecoration: true, }, }), diff --git a/src/infra/outbound/target-normalization.test.ts b/src/infra/outbound/target-normalization.test.ts index d3c37463689..7eacdb6d184 100644 --- a/src/infra/outbound/target-normalization.test.ts +++ b/src/infra/outbound/target-normalization.test.ts @@ -51,7 +51,7 @@ describe("normalizeChannelTargetInput", () => { describe("normalizeTargetForProvider", () => { it.each([undefined, " "])("returns undefined for blank raw input %j", (raw) => { - expect(normalizeTargetForProvider("telegram", raw)).toBeUndefined(); + expect(normalizeTargetForProvider("alpha", raw)).toBeUndefined(); }); it.each([ @@ -63,7 +63,7 @@ describe("normalizeTargetForProvider", () => { expected: "raw-id", }, { - provider: "telegram", + provider: "alpha", setup: () => { getActivePluginChannelRegistryVersionMock.mockReturnValueOnce(1); getChannelPluginMock.mockReturnValueOnce(undefined); @@ -93,9 +93,9 @@ describe("normalizeTargetForProvider", () => { messaging: { normalizeTarget: secondNormalizer }, }); - expect(normalizeTargetForProvider("telegram", " abc ")).toBe("ABC"); - expect(normalizeTargetForProvider("telegram", " def ")).toBe("DEF"); - expect(normalizeTargetForProvider("telegram", " ghi ")).toBe("next:ghi"); + expect(normalizeTargetForProvider("alpha", " abc ")).toBe("ABC"); + expect(normalizeTargetForProvider("alpha", " def ")).toBe("DEF"); + expect(normalizeTargetForProvider("alpha", " ghi ")).toBe("next:ghi"); expect(getChannelPluginMock).toHaveBeenCalledTimes(2); expect(firstNormalizer).toHaveBeenCalledTimes(2); @@ -110,13 +110,13 @@ describe("normalizeTargetForProvider", () => { }, }); - expect(normalizeTargetForProvider("telegram", " raw-id ")).toBeUndefined(); + expect(normalizeTargetForProvider("alpha", " raw-id ")).toBeUndefined(); }); }); describe("resolveNormalizedTargetInput", () => { it("returns undefined for blank input", () => { - expect(resolveNormalizedTargetInput("telegram", " ")).toBeUndefined(); + expect(resolveNormalizedTargetInput("alpha", " ")).toBeUndefined(); }); it("returns raw and normalized values", () => { @@ -127,7 +127,7 @@ describe("resolveNormalizedTargetInput", () => { }, }); - expect(resolveNormalizedTargetInput("telegram", " abc ")).toEqual({ + expect(resolveNormalizedTargetInput("alpha", " abc ")).toEqual({ raw: "abc", normalized: "ABC", }); @@ -147,7 +147,7 @@ describe("looksLikeTargetId", () => { expect( looksLikeTargetId({ - channel: "telegram", + channel: "alpha", raw: "room-1", normalized: "ROOM-1", }), @@ -159,7 +159,7 @@ describe("looksLikeTargetId", () => { "falls back to built-in id-like heuristics for %s", (raw) => { getChannelPluginMock.mockReturnValueOnce(undefined); - expect(looksLikeTargetId({ channel: "slack", raw })).toBe(true); + expect(looksLikeTargetId({ channel: "workspace", raw })).toBe(true); }, ); }); @@ -180,7 +180,7 @@ describe("maybeResolvePluginMessagingTarget", () => { await expect( maybeResolvePluginMessagingTarget({ cfg, - channel: "slack", + channel: "workspace", input: "general", requireIdLike: true, }), @@ -211,7 +211,7 @@ describe("maybeResolvePluginMessagingTarget", () => { await expect( maybeResolvePluginMessagingTarget({ cfg, - channel: "slack", + channel: "workspace", input: " channel:c123abc ", }), ).resolves.toEqual({ @@ -243,7 +243,7 @@ describe("buildTargetResolverSignature", () => { }, }); - const first = buildTargetResolverSignature("slack"); + const first = buildTargetResolverSignature("workspace"); getChannelPluginMock.mockReturnValueOnce({ messaging: { targetResolver: { @@ -252,7 +252,7 @@ describe("buildTargetResolverSignature", () => { }, }, }); - const second = buildTargetResolverSignature("slack"); + const second = buildTargetResolverSignature("workspace"); expect(first).toBe(second); }); @@ -266,7 +266,7 @@ describe("buildTargetResolverSignature", () => { }, }, }); - const first = buildTargetResolverSignature("slack"); + const first = buildTargetResolverSignature("workspace"); getChannelPluginMock.mockReturnValueOnce({ messaging: { @@ -276,7 +276,7 @@ describe("buildTargetResolverSignature", () => { }, }, }); - const second = buildTargetResolverSignature("slack"); + const second = buildTargetResolverSignature("workspace"); expect(first).not.toBe(second); });