diff --git a/extensions/discord/src/channel.ts b/extensions/discord/src/channel.ts index 9131ae42ee2..815dafbf667 100644 --- a/extensions/discord/src/channel.ts +++ b/extensions/discord/src/channel.ts @@ -23,6 +23,7 @@ import { resolveDiscordGroupRequireMention, resolveDiscordGroupToolPolicy, resolveOpenProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, setAccountEnabledInConfigSection, type ChannelMessageActionAdapter, type ChannelPlugin, @@ -131,7 +132,7 @@ export const discordPlugin: ChannelPlugin = { }, collectWarnings: ({ account, cfg }) => { const warnings: string[] = []; - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveOpenProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.discord !== undefined, groupPolicy: account.config.groupPolicy, diff --git a/extensions/feishu/src/bot.ts b/extensions/feishu/src/bot.ts index 14b4c95f0a7..2cf30c44067 100644 --- a/extensions/feishu/src/bot.ts +++ b/extensions/feishu/src/bot.ts @@ -7,6 +7,7 @@ import { type HistoryEntry, recordPendingHistoryEntryIfEnabled, resolveOpenProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, } from "openclaw/plugin-sdk"; import { resolveFeishuAccount } from "./accounts.js"; @@ -565,7 +566,7 @@ export async function handleFeishuMessage(params: { const useAccessGroups = cfg.commands?.useAccessGroups !== false; if (isGroup) { - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.feishu !== undefined, groupPolicy: feishuCfg?.groupPolicy, diff --git a/extensions/feishu/src/channel.ts b/extensions/feishu/src/channel.ts index c4437247608..f222924170f 100644 --- a/extensions/feishu/src/channel.ts +++ b/extensions/feishu/src/channel.ts @@ -5,6 +5,7 @@ import { DEFAULT_ACCOUNT_ID, PAIRING_APPROVED_MESSAGE, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, } from "openclaw/plugin-sdk"; import { resolveFeishuAccount, @@ -225,7 +226,7 @@ export const feishuPlugin: ChannelPlugin = { collectWarnings: ({ cfg, accountId }) => { const account = resolveFeishuAccount({ cfg, accountId }); const feishuCfg = account.config; - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.feishu !== undefined, groupPolicy: feishuCfg?.groupPolicy, diff --git a/extensions/googlechat/src/channel.ts b/extensions/googlechat/src/channel.ts index d8a9aed16aa..52943f63049 100644 --- a/extensions/googlechat/src/channel.ts +++ b/extensions/googlechat/src/channel.ts @@ -12,6 +12,7 @@ import { resolveChannelMediaMaxBytes, resolveGoogleChatGroupRequireMention, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, setAccountEnabledInConfigSection, type ChannelDock, type ChannelMessageActionAdapter, @@ -199,7 +200,7 @@ export const googlechatPlugin: ChannelPlugin = { }, collectWarnings: ({ account, cfg }) => { const warnings: string[] = []; - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.googlechat !== undefined, groupPolicy: account.config.groupPolicy, diff --git a/extensions/googlechat/src/monitor.ts b/extensions/googlechat/src/monitor.ts index 10501c8e1f2..689f10341c2 100644 --- a/extensions/googlechat/src/monitor.ts +++ b/extensions/googlechat/src/monitor.ts @@ -1,11 +1,13 @@ import type { IncomingMessage, ServerResponse } from "node:http"; import type { OpenClawConfig } from "openclaw/plugin-sdk"; import { + GROUP_POLICY_BLOCKED_LABEL, createReplyPrefixOptions, readJsonBodyWithLimit, registerWebhookTarget, rejectNonPostWebhookRequest, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, resolveSingleWebhookTargetAsync, resolveWebhookPath, resolveWebhookTargets, @@ -428,7 +430,7 @@ async function processMessageWithPipeline(params: { return; } - const defaultGroupPolicy = config.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(config); const { groupPolicy, providerMissingFallbackApplied } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: config.channels?.googlechat !== undefined, @@ -439,7 +441,7 @@ async function processMessageWithPipeline(params: { providerMissingFallbackApplied, providerKey: "googlechat", accountId: account.accountId, - blockedLabel: "space messages", + blockedLabel: GROUP_POLICY_BLOCKED_LABEL.space, log: (message) => logVerbose(core, runtime, message), }); const groupConfigResolved = resolveGroupConfig({ diff --git a/extensions/imessage/src/channel.ts b/extensions/imessage/src/channel.ts index 7cba0174000..a2b7bbde630 100644 --- a/extensions/imessage/src/channel.ts +++ b/extensions/imessage/src/channel.ts @@ -19,6 +19,7 @@ import { resolveIMessageGroupRequireMention, resolveIMessageGroupToolPolicy, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, setAccountEnabledInConfigSection, type ChannelPlugin, type ResolvedIMessageAccount, @@ -98,7 +99,7 @@ export const imessagePlugin: ChannelPlugin = { }; }, collectWarnings: ({ account, cfg }) => { - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.imessage !== undefined, groupPolicy: account.config.groupPolicy, diff --git a/extensions/irc/src/channel.ts b/extensions/irc/src/channel.ts index a9e7a4766ed..59121e7ff58 100644 --- a/extensions/irc/src/channel.ts +++ b/extensions/irc/src/channel.ts @@ -5,6 +5,7 @@ import { getChatChannelMeta, PAIRING_APPROVED_MESSAGE, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, setAccountEnabledInConfigSection, deleteAccountFromConfigSection, type ChannelPlugin, @@ -135,7 +136,7 @@ export const ircPlugin: ChannelPlugin = { }, collectWarnings: ({ account, cfg }) => { const warnings: string[] = []; - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.irc !== undefined, groupPolicy: account.config.groupPolicy, diff --git a/extensions/irc/src/inbound.ts b/extensions/irc/src/inbound.ts index 31586f01417..dd466f09507 100644 --- a/extensions/irc/src/inbound.ts +++ b/extensions/irc/src/inbound.ts @@ -1,8 +1,10 @@ import { + GROUP_POLICY_BLOCKED_LABEL, createReplyPrefixOptions, logInboundDrop, resolveControlCommandGate, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, type OpenClawConfig, type RuntimeEnv, @@ -86,7 +88,7 @@ export async function handleIrcInbound(params: { : message.senderNick; const dmPolicy = account.config.dmPolicy ?? "pairing"; - const defaultGroupPolicy = config.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(config); const { groupPolicy, providerMissingFallbackApplied } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: config.channels?.irc !== undefined, @@ -97,7 +99,7 @@ export async function handleIrcInbound(params: { providerMissingFallbackApplied, providerKey: "irc", accountId: account.accountId, - blockedLabel: "channel messages", + blockedLabel: GROUP_POLICY_BLOCKED_LABEL.channel, log: (message) => runtime.log?.(message), }); diff --git a/extensions/line/src/channel.ts b/extensions/line/src/channel.ts index a2a73a87eb9..ac49940d256 100644 --- a/extensions/line/src/channel.ts +++ b/extensions/line/src/channel.ts @@ -4,6 +4,7 @@ import { LineConfigSchema, processLineMessage, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, type ChannelPlugin, type ChannelStatusIssue, type OpenClawConfig, @@ -162,7 +163,7 @@ export const linePlugin: ChannelPlugin = { }; }, collectWarnings: ({ account, cfg }) => { - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.line !== undefined, groupPolicy: account.config.groupPolicy, diff --git a/extensions/matrix/src/channel.ts b/extensions/matrix/src/channel.ts index 7547d6f0260..20dde4dc6ed 100644 --- a/extensions/matrix/src/channel.ts +++ b/extensions/matrix/src/channel.ts @@ -7,6 +7,7 @@ import { normalizeAccountId, PAIRING_APPROVED_MESSAGE, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, setAccountEnabledInConfigSection, type ChannelPlugin, } from "openclaw/plugin-sdk"; @@ -170,7 +171,7 @@ export const matrixPlugin: ChannelPlugin = { }; }, collectWarnings: ({ account, cfg }) => { - const defaultGroupPolicy = (cfg as CoreConfig).channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg as CoreConfig); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: (cfg as CoreConfig).channels?.matrix !== undefined, groupPolicy: account.config.groupPolicy, diff --git a/extensions/matrix/src/matrix/monitor/index.ts b/extensions/matrix/src/matrix/monitor/index.ts index eba8b3703f6..0544dba9ab2 100644 --- a/extensions/matrix/src/matrix/monitor/index.ts +++ b/extensions/matrix/src/matrix/monitor/index.ts @@ -1,7 +1,9 @@ import { format } from "node:util"; import { + GROUP_POLICY_BLOCKED_LABEL, mergeAllowlist, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, summarizeMapping, warnMissingProviderGroupPolicyFallbackOnce, type RuntimeEnv, @@ -248,7 +250,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi setActiveMatrixClient(client, opts.accountId); const mentionRegexes = core.channel.mentions.buildMentionRegexes(cfg); - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy: groupPolicyRaw, providerMissingFallbackApplied } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.matrix !== undefined, @@ -259,7 +261,7 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi providerMissingFallbackApplied, providerKey: "matrix", accountId: account.accountId, - blockedLabel: "room messages", + blockedLabel: GROUP_POLICY_BLOCKED_LABEL.room, log: (message) => logVerboseMessage(message), }); const groupPolicy = allowlistOnly && groupPolicyRaw === "open" ? "allowlist" : groupPolicyRaw; diff --git a/extensions/mattermost/src/channel.ts b/extensions/mattermost/src/channel.ts index 4fcc38d189a..5053026f49a 100644 --- a/extensions/mattermost/src/channel.ts +++ b/extensions/mattermost/src/channel.ts @@ -7,6 +7,7 @@ import { migrateBaseNameToDefaultAccount, normalizeAccountId, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, setAccountEnabledInConfigSection, type ChannelMessageActionAdapter, type ChannelMessageActionName, @@ -229,7 +230,7 @@ export const mattermostPlugin: ChannelPlugin = { }; }, collectWarnings: ({ account, cfg }) => { - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.mattermost !== undefined, groupPolicy: account.config.groupPolicy, diff --git a/extensions/mattermost/src/mattermost/monitor.ts b/extensions/mattermost/src/mattermost/monitor.ts index 176d0e19d73..2ae8388b0fb 100644 --- a/extensions/mattermost/src/mattermost/monitor.ts +++ b/extensions/mattermost/src/mattermost/monitor.ts @@ -17,6 +17,7 @@ import { recordPendingHistoryEntryIfEnabled, resolveControlCommandGate, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, resolveChannelMediaMaxBytes, warnMissingProviderGroupPolicyFallbackOnce, type HistoryEntry, @@ -244,7 +245,7 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {} cfg.messages?.groupChat?.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT, ); const channelHistories = new Map(); - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy, providerMissingFallbackApplied } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.mattermost !== undefined, diff --git a/extensions/msteams/src/channel.ts b/extensions/msteams/src/channel.ts index b0aff91dd85..16c7ad0fb49 100644 --- a/extensions/msteams/src/channel.ts +++ b/extensions/msteams/src/channel.ts @@ -7,6 +7,7 @@ import { MSTeamsConfigSchema, PAIRING_APPROVED_MESSAGE, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, } from "openclaw/plugin-sdk"; import { listMSTeamsDirectoryGroupsLive, listMSTeamsDirectoryPeersLive } from "./directory-live.js"; import { msteamsOnboardingAdapter } from "./onboarding.js"; @@ -128,7 +129,7 @@ export const msteamsPlugin: ChannelPlugin = { }, security: { collectWarnings: ({ cfg }) => { - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.msteams !== undefined, groupPolicy: cfg.channels?.msteams?.groupPolicy, diff --git a/extensions/msteams/src/monitor-handler/message-handler.ts b/extensions/msteams/src/monitor-handler/message-handler.ts index ae1f203a016..56f9848dd71 100644 --- a/extensions/msteams/src/monitor-handler/message-handler.ts +++ b/extensions/msteams/src/monitor-handler/message-handler.ts @@ -5,6 +5,7 @@ import { logInboundDrop, recordPendingHistoryEntryIfEnabled, resolveControlCommandGate, + resolveDefaultGroupPolicy, resolveMentionGating, formatAllowlistMatchMeta, type HistoryEntry, @@ -174,7 +175,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) { } } - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const groupPolicy = !isDirectMessage && msteamsCfg ? (msteamsCfg.groupPolicy ?? defaultGroupPolicy ?? "allowlist") diff --git a/extensions/nextcloud-talk/src/channel.ts b/extensions/nextcloud-talk/src/channel.ts index eb55a4cbd75..c0cfa8e44be 100644 --- a/extensions/nextcloud-talk/src/channel.ts +++ b/extensions/nextcloud-talk/src/channel.ts @@ -6,6 +6,7 @@ import { formatPairingApproveHint, normalizeAccountId, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, setAccountEnabledInConfigSection, type ChannelPlugin, type OpenClawConfig, @@ -129,7 +130,7 @@ export const nextcloudTalkPlugin: ChannelPlugin = }; }, collectWarnings: ({ account, cfg }) => { - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: (cfg.channels as Record | undefined)?.["nextcloud-talk"] !== undefined, diff --git a/extensions/nextcloud-talk/src/inbound.ts b/extensions/nextcloud-talk/src/inbound.ts index 20195c9b817..5ad02979b60 100644 --- a/extensions/nextcloud-talk/src/inbound.ts +++ b/extensions/nextcloud-talk/src/inbound.ts @@ -1,8 +1,10 @@ import { + GROUP_POLICY_BLOCKED_LABEL, createReplyPrefixOptions, logInboundDrop, resolveControlCommandGate, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, type OpenClawConfig, type RuntimeEnv, @@ -86,11 +88,7 @@ export async function handleNextcloudTalkInbound(params: { statusSink?.({ lastInboundAt: message.timestamp }); const dmPolicy = account.config.dmPolicy ?? "pairing"; - const defaultGroupPolicy = ( - (config.channels as Record | undefined)?.defaults as - | { groupPolicy?: string } - | undefined - )?.groupPolicy as GroupPolicy | undefined; + const defaultGroupPolicy = resolveDefaultGroupPolicy(config as OpenClawConfig); const { groupPolicy, providerMissingFallbackApplied } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: @@ -103,7 +101,7 @@ export async function handleNextcloudTalkInbound(params: { providerMissingFallbackApplied, providerKey: "nextcloud-talk", accountId: account.accountId, - blockedLabel: "room messages", + blockedLabel: GROUP_POLICY_BLOCKED_LABEL.room, log: (message) => runtime.log?.(message), }); diff --git a/extensions/signal/src/channel.ts b/extensions/signal/src/channel.ts index 01426dd7ebc..2feb30dfe95 100644 --- a/extensions/signal/src/channel.ts +++ b/extensions/signal/src/channel.ts @@ -18,6 +18,7 @@ import { resolveChannelMediaMaxBytes, resolveDefaultSignalAccountId, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, resolveSignalAccount, setAccountEnabledInConfigSection, signalOnboardingAdapter, @@ -124,7 +125,7 @@ export const signalPlugin: ChannelPlugin = { }; }, collectWarnings: ({ account, cfg }) => { - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.signal !== undefined, groupPolicy: account.config.groupPolicy, diff --git a/extensions/slack/src/channel.ts b/extensions/slack/src/channel.ts index 050fa213e28..f431f71b3c9 100644 --- a/extensions/slack/src/channel.ts +++ b/extensions/slack/src/channel.ts @@ -20,6 +20,7 @@ import { resolveSlackAccount, resolveSlackReplyToMode, resolveOpenProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, resolveSlackGroupRequireMention, resolveSlackGroupToolPolicy, buildSlackThreadingToolContext, @@ -151,7 +152,7 @@ export const slackPlugin: ChannelPlugin = { }, collectWarnings: ({ account, cfg }) => { const warnings: string[] = []; - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveOpenProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.slack !== undefined, groupPolicy: account.config.groupPolicy, diff --git a/extensions/telegram/src/channel.ts b/extensions/telegram/src/channel.ts index 9836e0e139b..91ccba95e01 100644 --- a/extensions/telegram/src/channel.ts +++ b/extensions/telegram/src/channel.ts @@ -18,6 +18,7 @@ import { parseTelegramThreadId, resolveDefaultTelegramAccountId, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, resolveTelegramAccount, resolveTelegramGroupRequireMention, resolveTelegramGroupToolPolicy, @@ -196,7 +197,7 @@ export const telegramPlugin: ChannelPlugin { - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.telegram !== undefined, groupPolicy: account.config.groupPolicy, diff --git a/extensions/whatsapp/src/channel.ts b/extensions/whatsapp/src/channel.ts index d7abf02b031..b122577e2e8 100644 --- a/extensions/whatsapp/src/channel.ts +++ b/extensions/whatsapp/src/channel.ts @@ -20,6 +20,7 @@ import { resolveDefaultWhatsAppAccountId, resolveWhatsAppOutboundTarget, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, resolveWhatsAppAccount, resolveWhatsAppGroupRequireMention, resolveWhatsAppGroupToolPolicy, @@ -143,7 +144,7 @@ export const whatsappPlugin: ChannelPlugin = { }; }, collectWarnings: ({ account, cfg }) => { - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.whatsapp !== undefined, groupPolicy: account.groupPolicy, diff --git a/extensions/zalouser/src/monitor.ts b/extensions/zalouser/src/monitor.ts index ba2ee890e73..17575c40128 100644 --- a/extensions/zalouser/src/monitor.ts +++ b/extensions/zalouser/src/monitor.ts @@ -4,6 +4,7 @@ import { createReplyPrefixOptions, mergeAllowlist, resolveOpenProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, resolveSenderCommandAuthorization, summarizeMapping, warnMissingProviderGroupPolicyFallbackOnce, @@ -179,7 +180,7 @@ async function processMessage( const groupName = metadata?.threadName ?? ""; const chatId = threadId; - const defaultGroupPolicy = config.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(config); const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({ providerConfigPresent: config.channels?.zalouser !== undefined, groupPolicy: account.config.groupPolicy, diff --git a/src/config/runtime-group-policy.test.ts b/src/config/runtime-group-policy.test.ts index 230954ca3b9..5475fc0643d 100644 --- a/src/config/runtime-group-policy.test.ts +++ b/src/config/runtime-group-policy.test.ts @@ -1,11 +1,18 @@ -import { describe, expect, it } from "vitest"; +import { beforeEach, describe, expect, it } from "vitest"; import { + GROUP_POLICY_BLOCKED_LABEL, + resetMissingProviderGroupPolicyFallbackWarningsForTesting, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, resolveRuntimeGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, } from "./runtime-group-policy.js"; +beforeEach(() => { + resetMissingProviderGroupPolicyFallbackWarningsForTesting(); +}); + describe("resolveRuntimeGroupPolicy", () => { it.each([ { @@ -58,6 +65,15 @@ describe("resolveAllowlistProviderRuntimeGroupPolicy", () => { }); }); +describe("resolveDefaultGroupPolicy", () => { + it("returns channels.defaults.groupPolicy when present", () => { + const resolved = resolveDefaultGroupPolicy({ + channels: { defaults: { groupPolicy: "disabled" } }, + }); + expect(resolved).toBe("disabled"); + }); +}); + describe("warnMissingProviderGroupPolicyFallbackOnce", () => { it("logs only once per provider/account key", () => { const lines: string[] = []; @@ -65,14 +81,14 @@ describe("warnMissingProviderGroupPolicyFallbackOnce", () => { providerMissingFallbackApplied: true, providerKey: "runtime-policy-test", accountId: "account-a", - blockedLabel: "room messages", + blockedLabel: GROUP_POLICY_BLOCKED_LABEL.room, log: (message) => lines.push(message), }); const second = warnMissingProviderGroupPolicyFallbackOnce({ providerMissingFallbackApplied: true, providerKey: "runtime-policy-test", accountId: "account-a", - blockedLabel: "room messages", + blockedLabel: GROUP_POLICY_BLOCKED_LABEL.room, log: (message) => lines.push(message), }); diff --git a/src/config/runtime-group-policy.ts b/src/config/runtime-group-policy.ts index c2658f3862a..62ee6db7d8a 100644 --- a/src/config/runtime-group-policy.ts +++ b/src/config/runtime-group-policy.ts @@ -32,6 +32,26 @@ export type ResolveProviderRuntimeGroupPolicyParams = { defaultGroupPolicy?: GroupPolicy; }; +export type GroupPolicyDefaultsConfig = { + channels?: { + defaults?: { + groupPolicy?: GroupPolicy; + }; + }; +}; + +export function resolveDefaultGroupPolicy(cfg: GroupPolicyDefaultsConfig): GroupPolicy | undefined { + return cfg.channels?.defaults?.groupPolicy; +} + +export const GROUP_POLICY_BLOCKED_LABEL = { + group: "group messages", + guild: "guild messages", + room: "room messages", + channel: "channel messages", + space: "space messages", +} as const; + /** * Standard provider runtime policy: * - configured provider fallback: open @@ -89,3 +109,10 @@ export function warnMissingProviderGroupPolicyFallbackOnce(params: { ); return true; } + +/** + * Test helper. Keeps warning-cache state deterministic across test files. + */ +export function resetMissingProviderGroupPolicyFallbackWarningsForTesting(): void { + warnedMissingProviderGroupPolicy.clear(); +} diff --git a/src/discord/monitor/provider.ts b/src/discord/monitor/provider.ts index 6fab5af9e67..828b35759e6 100644 --- a/src/discord/monitor/provider.ts +++ b/src/discord/monitor/provider.ts @@ -22,7 +22,9 @@ import { import type { OpenClawConfig, ReplyToMode } from "../../config/config.js"; import { loadConfig } from "../../config/config.js"; import { + GROUP_POLICY_BLOCKED_LABEL, resolveOpenProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, } from "../../config/runtime-group-policy.js"; import { danger, logVerbose, shouldLogVerbose, warn } from "../../globals.js"; @@ -256,7 +258,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) { const discordRestFetch = resolveDiscordRestFetch(rawDiscordCfg.proxy, runtime); const dmConfig = rawDiscordCfg.dm; let guildEntries = rawDiscordCfg.guilds; - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const providerConfigPresent = cfg.channels?.discord !== undefined; const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({ providerConfigPresent, @@ -269,7 +271,7 @@ export async function monitorDiscordProvider(opts: MonitorDiscordOpts = {}) { providerMissingFallbackApplied, providerKey: "discord", accountId: account.accountId, - blockedLabel: "guild messages", + blockedLabel: GROUP_POLICY_BLOCKED_LABEL.guild, log: (message) => runtime.log?.(warn(message)), }); let allowFrom = discordCfg.allowFrom ?? dmConfig?.allowFrom; @@ -629,6 +631,7 @@ export const __testing = { createDiscordGatewayPlugin, dedupeSkillCommandsForDiscord, resolveDiscordRuntimeGroupPolicy: resolveOpenProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, resolveDiscordRestFetch, resolveThreadBindingsEnabled, }; diff --git a/src/imessage/monitor/monitor-provider.ts b/src/imessage/monitor/monitor-provider.ts index 69f568442a2..703935954b1 100644 --- a/src/imessage/monitor/monitor-provider.ts +++ b/src/imessage/monitor/monitor-provider.ts @@ -18,6 +18,7 @@ import { recordInboundSession } from "../../channels/session.js"; import { loadConfig } from "../../config/config.js"; import { resolveOpenProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, } from "../../config/runtime-group-policy.js"; import { readSessionUpdatedAt, resolveStorePath } from "../../config/sessions.js"; @@ -147,7 +148,7 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P imessageCfg.groupAllowFrom ?? (imessageCfg.allowFrom && imessageCfg.allowFrom.length > 0 ? imessageCfg.allowFrom : []), ); - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.imessage !== undefined, groupPolicy: imessageCfg.groupPolicy, @@ -525,4 +526,5 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P export const __testing = { resolveIMessageRuntimeGroupPolicy: resolveOpenProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, }; diff --git a/src/line/bot-handlers.ts b/src/line/bot-handlers.ts index b86a4f1a4ee..e6b30f42d20 100644 --- a/src/line/bot-handlers.ts +++ b/src/line/bot-handlers.ts @@ -10,6 +10,7 @@ import type { import type { OpenClawConfig } from "../config/config.js"; import { resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, } from "../config/runtime-group-policy.js"; import { danger, logVerbose } from "../globals.js"; @@ -136,7 +137,7 @@ async function shouldProcessLineEvent( storeAllowFrom, dmPolicy, }); - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy, providerMissingFallbackApplied } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.line !== undefined, diff --git a/src/plugin-sdk/index.ts b/src/plugin-sdk/index.ts index 7d64d5ffa27..01e890c8bad 100644 --- a/src/plugin-sdk/index.ts +++ b/src/plugin-sdk/index.ts @@ -133,9 +133,13 @@ export type { MSTeamsTeamConfig, } from "../config/types.js"; export { + GROUP_POLICY_BLOCKED_LABEL, + resetMissingProviderGroupPolicyFallbackWarningsForTesting, resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, resolveOpenProviderRuntimeGroupPolicy, resolveRuntimeGroupPolicy, + type GroupPolicyDefaultsConfig, type RuntimeGroupPolicyResolution, type RuntimeGroupPolicyParams, type ResolveProviderRuntimeGroupPolicyParams, diff --git a/src/signal/monitor.ts b/src/signal/monitor.ts index 8424e11cea4..461f38c2171 100644 --- a/src/signal/monitor.ts +++ b/src/signal/monitor.ts @@ -5,6 +5,7 @@ import type { OpenClawConfig } from "../config/config.js"; import { loadConfig } from "../config/config.js"; import { resolveAllowlistProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, } from "../config/runtime-group-policy.js"; import type { SignalReactionNotificationMode } from "../config/types.js"; @@ -348,7 +349,7 @@ export async function monitorSignalProvider(opts: MonitorSignalOpts = {}): Promi ? accountInfo.config.allowFrom : []), ); - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy, providerMissingFallbackApplied } = resolveAllowlistProviderRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.signal !== undefined, diff --git a/src/slack/monitor/provider.ts b/src/slack/monitor/provider.ts index 472d459b35d..35003bedf28 100644 --- a/src/slack/monitor/provider.ts +++ b/src/slack/monitor/provider.ts @@ -12,6 +12,7 @@ import { import { loadConfig } from "../../config/config.js"; import { resolveOpenProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, } from "../../config/runtime-group-policy.js"; import type { SessionScope } from "../../config/sessions.js"; @@ -102,7 +103,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) { const groupDmEnabled = dmConfig?.groupEnabled ?? false; const groupDmChannels = dmConfig?.groupChannels; let channelsConfig = slackCfg.channels; - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const providerConfigPresent = cfg.channels?.slack !== undefined; const { groupPolicy, providerMissingFallbackApplied } = resolveOpenProviderRuntimeGroupPolicy({ providerConfigPresent, @@ -369,4 +370,5 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) { export const __testing = { resolveSlackRuntimeGroupPolicy: resolveOpenProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, }; diff --git a/src/web/inbound/access-control.ts b/src/web/inbound/access-control.ts index e4f6454345b..794897a5388 100644 --- a/src/web/inbound/access-control.ts +++ b/src/web/inbound/access-control.ts @@ -1,6 +1,7 @@ import { loadConfig } from "../../config/config.js"; import { resolveOpenProviderRuntimeGroupPolicy, + resolveDefaultGroupPolicy, warnMissingProviderGroupPolicyFallbackOnce, } from "../../config/runtime-group-policy.js"; import { logVerbose } from "../../globals.js"; @@ -100,7 +101,7 @@ export async function checkInboundAccessControl(params: { // - "open": groups bypass allowFrom, only mention-gating applies // - "disabled": block all group messages entirely // - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom - const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy; + const defaultGroupPolicy = resolveDefaultGroupPolicy(cfg); const { groupPolicy, providerMissingFallbackApplied } = resolveWhatsAppRuntimeGroupPolicy({ providerConfigPresent: cfg.channels?.whatsapp !== undefined, groupPolicy: account.groupPolicy,