diff --git a/CHANGELOG.md b/CHANGELOG.md index eaebd02e63c..f6ad34b619e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ Docs: https://docs.openclaw.ai - Slack/capabilities: read granted scopes from `auth.test` response metadata before trying legacy scope APIs, so modern bot tokens no longer report `unknown_method` for channel capabilities. Fixes #44625. Thanks @Qquanwei and @martingarramon. - Slack/DMs: send text/block-only proactive DMs directly with `chat.postMessage(channel=)` while keeping conversation resolution for uploads and threaded sends. Fixes #62042. Thanks @MarkMolina. - Slack/routing: match route bindings written with Slack target syntax such as `channel:C...`, `user:U...`, or `<@U...>`, so bound Slack peers route to the configured agent instead of `main`. Fixes #41608. Thanks @Winnsolutionsadmin. +- Slack/routing: match public-channel allowlist entries written as `channel:C...` against bare Slack runtime channel IDs, so allowed channel mentions do not fail as `channel-not-allowed`. Fixes #41264 and supersedes #56530. Thanks @babutree and @Realworld404. - Slack/message actions: prefer the account bound to the outbound target peer before falling back to the agent's first channel account, so multi-workspace sends use the intended Slack account. Supersedes #66807. Thanks @rijhsinghani. - Slack/delivery: retry Slack Web API writes only when the SDK wraps a DNS request failure such as `EAI_AGAIN`, so transient resolver hiccups can recover without retrying platform errors that may duplicate messages. Fixes #68789. Thanks @sonnyb9. - Slack/message actions: forward agent-scoped media roots through the bundled upload-file action path, so workspace files can be attached without failing the local-media guard. Fixes #64625. Thanks @benpchandler. diff --git a/extensions/slack/src/monitor/channel-config.ts b/extensions/slack/src/monitor/channel-config.ts index 38eed215194..05221793944 100644 --- a/extensions/slack/src/monitor/channel-config.ts +++ b/extensions/slack/src/monitor/channel-config.ts @@ -74,10 +74,16 @@ export function resolveSlackChannelConfig(params: { // entry-scan. buildChannelKeyCandidates deduplicates identical keys. const channelIdLower = normalizeLowercaseStringOrEmpty(channelId); const channelIdUpper = channelId.toUpperCase(); + const channelTarget = `channel:${channelId}`; + const channelTargetLower = `channel:${channelIdLower}`; + const channelTargetUpper = `channel:${channelIdUpper}`; const candidates = buildChannelKeyCandidates( channelId, channelIdLower !== channelId ? channelIdLower : undefined, channelIdUpper !== channelId ? channelIdUpper : undefined, + channelTarget, + channelTargetLower !== channelTarget ? channelTargetLower : undefined, + channelTargetUpper !== channelTarget ? channelTargetUpper : undefined, allowNameMatching ? (channelName ? `#${directName}` : undefined) : undefined, allowNameMatching ? directName : undefined, allowNameMatching ? normalizedName : undefined, diff --git a/extensions/slack/src/monitor/monitor.test.ts b/extensions/slack/src/monitor/monitor.test.ts index 4520f38cfca..f22113f0b07 100644 --- a/extensions/slack/src/monitor/monitor.test.ts +++ b/extensions/slack/src/monitor/monitor.test.ts @@ -79,6 +79,34 @@ describe("resolveSlackChannelConfig", () => { expect(res).toMatchObject({ allowed: true, requireMention: false }); }); + it("matches channel-prefixed config keys when Slack delivers a bare channel ID", () => { + const res = resolveSlackChannelConfig({ + channelId: "C0AJYR3BVTJ", + channels: { "channel:C0AJYR3BVTJ": { enabled: true, requireMention: false } }, + defaultRequireMention: true, + }); + expect(res).toMatchObject({ + allowed: true, + requireMention: false, + matchKey: "channel:C0AJYR3BVTJ", + matchSource: "direct", + }); + }); + + it("matches lowercase channel-prefixed config keys when Slack delivers uppercase channel IDs", () => { + const res = resolveSlackChannelConfig({ + channelId: "C0AJYR3BVTJ", + channels: { "channel:c0ajyr3bvtj": { enabled: true, requireMention: false } }, + defaultRequireMention: true, + }); + expect(res).toMatchObject({ + allowed: true, + requireMention: false, + matchKey: "channel:c0ajyr3bvtj", + matchSource: "direct", + }); + }); + it("blocks channel-name route matches by default", () => { const res = resolveSlackChannelConfig({ channelId: "C1",