refactor: unify channel config matching and gating

Co-authored-by: thewilloftheshadow <thewilloftheshadow@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-01-18 01:21:27 +00:00
parent 05f49d2846
commit f73dbdbaea
24 changed files with 430 additions and 120 deletions

View File

@@ -42,4 +42,16 @@ describe("resolveSlackChannelConfig", () => {
matchSource: "wildcard",
});
});
it("uses direct match metadata when channel config exists", () => {
const res = resolveSlackChannelConfig({
channelId: "C1",
channels: { C1: { allow: true, requireMention: false } },
defaultRequireMention: true,
});
expect(res).toMatchObject({
matchKey: "C1",
matchSource: "direct",
});
});
});

View File

@@ -2,7 +2,7 @@ import type { SlackReactionNotificationMode } from "../../config/config.js";
import type { SlackMessageEvent } from "../types.js";
import {
buildChannelKeyCandidates,
resolveChannelEntryMatch,
resolveChannelEntryMatchWithFallback,
} from "../../channels/channel-config.js";
import { allowListMatches, normalizeAllowListLower, normalizeSlackSlug } from "./allow-list.js";
@@ -91,10 +91,10 @@ export function resolveSlackChannelConfig(params: {
);
const {
entry: matched,
key: matchedKey,
wildcardEntry: fallback,
wildcardKey,
} = resolveChannelEntryMatch({
matchKey,
matchSource,
} = resolveChannelEntryMatchWithFallback({
entries,
keys: candidates,
wildcardKey: "*",
@@ -127,12 +127,9 @@ export function resolveSlackChannelConfig(params: {
skills,
systemPrompt,
};
if (matchedKey) {
result.matchKey = matchedKey;
result.matchSource = "direct";
} else if (wildcardKey && fallback) {
result.matchKey = wildcardKey;
result.matchSource = "wildcard";
if (matchKey) result.matchKey = matchKey;
if (matchSource === "direct" || matchSource === "wildcard") {
result.matchSource = matchSource;
}
return result;
}

View File

@@ -18,9 +18,9 @@ import { buildPairingReply } from "../../../pairing/pairing-messages.js";
import { upsertChannelPairingRequest } from "../../../pairing/pairing-store.js";
import { resolveAgentRoute } from "../../../routing/resolve-route.js";
import { resolveThreadSessionKeys } from "../../../routing/session-key.js";
import { resolveMentionGating } from "../../../channels/mention-gating.js";
import { resolveMentionGatingWithBypass } from "../../../channels/mention-gating.js";
import { resolveConversationLabel } from "../../../channels/conversation-label.js";
import { resolveCommandAuthorizedFromAuthorizers } from "../../../channels/command-gating.js";
import { resolveControlCommandGate } from "../../../channels/command-gating.js";
import type { ResolvedSlackAccount } from "../../accounts.js";
import { reactSlackMessage } from "../../actions.js";
@@ -229,6 +229,7 @@ export async function prepareSlackMessage(params: {
cfg,
surface: "slack",
});
const hasControlCommandInMessage = hasControlCommand(message.text ?? "", cfg);
const ownerAuthorized = resolveSlackAllowListMatch({
allowList: allowFromLower,
@@ -245,20 +246,18 @@ export async function prepareSlackMessage(params: {
userName: senderName,
})
: false;
const commandAuthorized = resolveCommandAuthorizedFromAuthorizers({
const commandGate = resolveControlCommandGate({
useAccessGroups: ctx.useAccessGroups,
authorizers: [
{ configured: allowFromLower.length > 0, allowed: ownerAuthorized },
{ configured: channelUsersAllowlistConfigured, allowed: channelCommandAuthorized },
],
allowTextCommands,
hasControlCommand: hasControlCommandInMessage,
});
const commandAuthorized = commandGate.commandAuthorized;
if (
allowTextCommands &&
isRoomish &&
hasControlCommand(message.text ?? "", cfg) &&
!commandAuthorized
) {
if (isRoomish && commandGate.shouldBlock) {
logVerbose(`Blocked slack control command from unauthorized sender ${senderId}`);
return null;
}
@@ -268,22 +267,17 @@ export async function prepareSlackMessage(params: {
: false;
// Allow "control commands" to bypass mention gating if sender is authorized.
const shouldBypassMention =
allowTextCommands &&
isRoom &&
shouldRequireMention &&
!wasMentioned &&
!hasAnyMention &&
commandAuthorized &&
hasControlCommand(message.text ?? "", cfg);
const canDetectMention = Boolean(ctx.botUserId) || mentionRegexes.length > 0;
const mentionGate = resolveMentionGating({
const mentionGate = resolveMentionGatingWithBypass({
isGroup: isRoom,
requireMention: Boolean(shouldRequireMention),
canDetectMention,
wasMentioned,
implicitMention,
shouldBypassMention,
hasAnyMention,
allowTextCommands,
hasControlCommand: hasControlCommandInMessage,
commandAuthorized,
});
const effectiveWasMentioned = mentionGate.effectiveWasMentioned;
if (isRoom && shouldRequireMention && mentionGate.shouldSkip) {