diff --git a/src/discord/monitor.test.ts b/src/discord/monitor.test.ts index e5c3ad347..be0c8aa65 100644 --- a/src/discord/monitor.test.ts +++ b/src/discord/monitor.test.ts @@ -299,6 +299,7 @@ describe("discord guild/channel resolution", () => { expect(general?.allowed).toBe(true); expect(general?.requireMention).toBe(false); expect(general?.autoThread).toBeUndefined(); + expect(general?.matchSource).toBe("direct"); // Unknown channel should use wildcard const random = resolveDiscordChannelConfig({ @@ -310,6 +311,28 @@ describe("discord guild/channel resolution", () => { expect(random?.allowed).toBe(true); expect(random?.autoThread).toBe(true); expect(random?.requireMention).toBe(true); + expect(random?.matchSource).toBe("wildcard"); + }); + + it("falls back to wildcard when thread channel and parent are missing", () => { + const guildInfo: DiscordGuildEntryResolved = { + channels: { + "*": { allow: true, requireMention: false }, + }, + }; + const thread = resolveDiscordChannelConfigWithFallback({ + guildInfo, + channelId: "thread-123", + channelName: "topic", + channelSlug: "topic", + parentId: "parent-999", + parentName: "general", + parentSlug: "general", + scope: "thread", + }); + expect(thread?.allowed).toBe(true); + expect(thread?.matchKey).toBe("*"); + expect(thread?.matchSource).toBe("wildcard"); }); }); diff --git a/src/discord/monitor/allow-list.ts b/src/discord/monitor/allow-list.ts index b3dc048e7..236cac15e 100644 --- a/src/discord/monitor/allow-list.ts +++ b/src/discord/monitor/allow-list.ts @@ -3,6 +3,7 @@ import type { Guild, User } from "@buape/carbon"; import { buildChannelKeyCandidates, resolveChannelEntryMatchWithFallback, + type ChannelMatchSource, } from "../../channels/channel-config.js"; import type { AllowlistMatch } from "../../channels/allowlist-match.js"; import { formatDiscordUserTag } from "./format.js"; @@ -44,7 +45,7 @@ export type DiscordChannelConfigResolved = { systemPrompt?: string; autoThread?: boolean; matchKey?: string; - matchSource?: "direct" | "parent"; + matchSource?: ChannelMatchSource; }; export function normalizeDiscordAllowList( @@ -198,13 +199,14 @@ function resolveDiscordChannelEntryMatch( entries: channels, keys, parentKeys, + wildcardKey: "*", }); } function resolveDiscordChannelConfigEntry( entry: DiscordChannelEntry, matchKey: string | undefined, - matchSource: "direct" | "parent", + matchSource: ChannelMatchSource, ): DiscordChannelConfigResolved { const resolved: DiscordChannelConfigResolved = { allowed: entry.allow !== false, @@ -234,15 +236,8 @@ export function resolveDiscordChannelConfig(params: { name: channelName, slug: channelSlug, }); - if (!match.entry || !match.matchKey) { - // Wildcard fallback: apply to all channels if "*" is configured - const wildcard = channels["*"]; - if (wildcard) { - return resolveDiscordChannelConfigEntry(wildcard, "*", "direct"); - } - return { allowed: false }; - } - return resolveDiscordChannelConfigEntry(match.entry, match.matchKey, "direct"); + if (!match.entry || !match.matchKey || !match.matchSource) return { allowed: false }; + return resolveDiscordChannelConfigEntry(match.entry, match.matchKey, match.matchSource); } export function resolveDiscordChannelConfigWithFallback(params: { @@ -285,16 +280,7 @@ export function resolveDiscordChannelConfigWithFallback(params: { : undefined, ); if (match.entry && match.matchKey && match.matchSource) { - return resolveDiscordChannelConfigEntry( - match.entry, - match.matchKey, - match.matchSource === "parent" ? "parent" : "direct", - ); - } - // Wildcard fallback: apply to all channels if "*" is configured - const wildcard = channels["*"]; - if (wildcard) { - return resolveDiscordChannelConfigEntry(wildcard, "*", "direct"); + return resolveDiscordChannelConfigEntry(match.entry, match.matchKey, match.matchSource); } return { allowed: false }; }