refactor: extend channel plugin boundary

This commit is contained in:
Peter Steinberger
2026-01-20 11:49:31 +00:00
parent 439044068a
commit 9a2bf57e1c
31 changed files with 234 additions and 162 deletions

View File

@@ -1,7 +1,8 @@
import type { ChannelId } from "../channels/plugins/types.js";
import { normalizeAccountId } from "../routing/session-key.js";
import type { ClawdbotConfig } from "./config.js";
export type GroupPolicyChannel = "whatsapp" | "telegram" | "imessage" | "bluebubbles";
export type GroupPolicyChannel = ChannelId;
export type ChannelGroupConfig = {
requireMention?: boolean;

View File

@@ -60,7 +60,7 @@ describe("applyPluginAutoEnable", () => {
expect(result.changes).toEqual([]);
});
describe("BlueBubbles over imessage prioritization", () => {
describe("preferOver channel prioritization", () => {
it("prefers bluebubbles: skips imessage auto-enable when both are configured", () => {
const result = applyPluginAutoEnable({
config: {

View File

@@ -1,6 +1,10 @@
import type { ClawdbotConfig } from "./config.js";
import { getChatChannelMeta, listChatChannels, normalizeChatChannelId } from "../channels/registry.js";
import {
getChannelPluginCatalogEntry,
listChannelPluginCatalogEntries,
} from "../channels/plugins/catalog.js";
import { normalizeProviderId } from "../agents/model-selection.js";
import { listChatChannels } from "../channels/registry.js";
import { hasAnyWhatsAppAuth } from "../web/accounts.js";
type PluginEnableChange = {
@@ -13,6 +17,13 @@ export type PluginAutoEnableResult = {
changes: string[];
};
const CHANNEL_PLUGIN_IDS = Array.from(
new Set([
...listChatChannels().map((meta) => meta.id),
...listChannelPluginCatalogEntries().map((entry) => entry.id),
]),
);
const PROVIDER_PLUGIN_IDS: Array<{ pluginId: string; providerId: string }> = [
{ pluginId: "google-antigravity-auth", providerId: "google-antigravity" },
{ pluginId: "google-gemini-cli-auth", providerId: "google-gemini-cli" },
@@ -226,10 +237,7 @@ function resolveConfiguredPlugins(
env: NodeJS.ProcessEnv,
): PluginEnableChange[] {
const changes: PluginEnableChange[] = [];
const channelIds = new Set<string>();
for (const meta of listChatChannels()) {
channelIds.add(meta.id);
}
const channelIds = new Set(CHANNEL_PLUGIN_IDS);
const configuredChannels = cfg.channels as Record<string, unknown> | undefined;
if (configuredChannels && typeof configuredChannels === "object") {
for (const key of Object.keys(configuredChannels)) {
@@ -267,21 +275,30 @@ function isPluginDenied(cfg: ClawdbotConfig, pluginId: string): boolean {
return Array.isArray(deny) && deny.includes(pluginId);
}
/**
* When both BlueBubbles and iMessage are configured, prefer BlueBubbles:
* skip auto-enabling iMessage unless BlueBubbles is explicitly disabled/denied.
* This is non-destructive: if iMessage is already enabled, it won't be touched.
*/
function shouldSkipImsgForBlueBubbles(
function resolvePreferredOverIds(pluginId: string): string[] {
const normalized = normalizeChatChannelId(pluginId);
if (normalized) {
return getChatChannelMeta(normalized).preferOver ?? [];
}
const catalogEntry = getChannelPluginCatalogEntry(pluginId);
return catalogEntry?.meta.preferOver ?? [];
}
function shouldSkipPreferredPluginAutoEnable(
cfg: ClawdbotConfig,
pluginId: string,
entry: PluginEnableChange,
configured: PluginEnableChange[],
): boolean {
if (pluginId !== "imessage") return false;
const blueBubblesConfigured = configured.some((e) => e.pluginId === "bluebubbles");
if (!blueBubblesConfigured) return false;
// Skip imessage auto-enable if bluebubbles is configured and not blocked
return !isPluginExplicitlyDisabled(cfg, "bluebubbles") && !isPluginDenied(cfg, "bluebubbles");
for (const other of configured) {
if (other.pluginId === entry.pluginId) continue;
if (isPluginDenied(cfg, other.pluginId)) continue;
if (isPluginExplicitlyDisabled(cfg, other.pluginId)) continue;
const preferOver = resolvePreferredOverIds(other.pluginId);
if (preferOver.includes(entry.pluginId)) {
return true;
}
}
return false;
}
function ensureAllowlisted(cfg: ClawdbotConfig, pluginId: string): ClawdbotConfig {
@@ -334,8 +351,7 @@ export function applyPluginAutoEnable(params: {
for (const entry of configured) {
if (isPluginDenied(next, entry.pluginId)) continue;
if (isPluginExplicitlyDisabled(next, entry.pluginId)) continue;
// Prefer BlueBubbles over imessage: skip imsg auto-enable if bluebubbles is configured
if (shouldSkipImsgForBlueBubbles(next, entry.pluginId, configured)) continue;
if (shouldSkipPreferredPluginAutoEnable(next, entry, configured)) continue;
const allow = next.plugins?.allow;
const allowMissing = Array.isArray(allow) && !allow.includes(entry.pluginId);
const alreadyEnabled = next.plugins?.entries?.[entry.pluginId]?.enabled === true;