mirror of https://github.com/openclaw/openclaw.git
refactor(doctor): centralize channel capability metadata (#52325)
* refactor(doctor): centralize channel capabilities * fix(doctor): preserve msteams sender warnings
This commit is contained in:
parent
d3a0a623a3
commit
b9e71240ed
|
|
@ -113,6 +113,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Telegram/setup: warn when setup leaves DMs on pairing without an allowlist, and show valid account-scoped remediation commands. (#50710) Thanks @ernestodeoliveira.
|
||||
- Doctor/Telegram: replace the fresh-install empty group-allowlist false positive with first-run guidance that explains DM pairing approval and the next group setup steps, so new Telegram installs get actionable setup help instead of a broken-config warning. Thanks @vincentkoc.
|
||||
- Doctor/extensions: keep Matrix DM `allowFrom` repairs on the canonical `dm.allowFrom` path and stop treating Zalouser group sender gating as if it fell back to `allowFrom`, so doctor warnings and `--fix` stay aligned with runtime access control. Thanks @vincentkoc.
|
||||
- Doctor/refactor: centralize built-in channel doctor semantics in one static capability registry with conservative fallback behavior for unknown/external channels, so future extension changes stop depending on scattered shared string checks. Thanks @vincentkoc.
|
||||
- Models/OpenRouter runtime capabilities: fetch uncatalogued OpenRouter model metadata on first use so newly added vision models keep image input instead of silently degrading to text-only, with top-level capability field fallbacks for `/api/v1/models`. (#45824) Thanks @DJjjjhao.
|
||||
- Channels/plugins: keep shared interactive payloads merge-ready by fixing Slack custom callback routing and repeat-click dedupe, allowing interactive-only sends, and preserving ordered Discord shared text blocks. (#47715) Thanks @vincentkoc.
|
||||
- Slack/interactive replies: preserve `channelData.slack.blocks` through live DM delivery and preview-finalized edits so Block Kit button and select directives render instead of falling back to raw text. (#45890) Thanks @vincentkoc.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { getDoctorChannelCapabilities } from "./channel-capabilities.js";
|
||||
|
||||
describe("doctor channel capabilities", () => {
|
||||
it("returns built-in capability overrides for matrix", () => {
|
||||
expect(getDoctorChannelCapabilities("matrix")).toEqual({
|
||||
dmAllowFromMode: "nestedOnly",
|
||||
groupModel: "sender",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("returns hybrid group semantics for zalouser", () => {
|
||||
expect(getDoctorChannelCapabilities("zalouser")).toEqual({
|
||||
dmAllowFromMode: "topOnly",
|
||||
groupModel: "hybrid",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves empty sender allowlist warnings for msteams hybrid routing", () => {
|
||||
expect(getDoctorChannelCapabilities("msteams")).toEqual({
|
||||
dmAllowFromMode: "topOnly",
|
||||
groupModel: "hybrid",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back conservatively for unknown external channels", () => {
|
||||
expect(getDoctorChannelCapabilities("external-demo")).toEqual({
|
||||
dmAllowFromMode: "topOnly",
|
||||
groupModel: "sender",
|
||||
groupAllowFromFallbackToAllowFrom: true,
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
import type { AllowFromMode } from "./shared/allow-from-mode.js";
|
||||
|
||||
export type DoctorGroupModel = "sender" | "route" | "hybrid";
|
||||
|
||||
export type DoctorChannelCapabilities = {
|
||||
dmAllowFromMode: AllowFromMode;
|
||||
groupModel: DoctorGroupModel;
|
||||
groupAllowFromFallbackToAllowFrom: boolean;
|
||||
warnOnEmptyGroupSenderAllowlist: boolean;
|
||||
};
|
||||
|
||||
const DEFAULT_DOCTOR_CHANNEL_CAPABILITIES: DoctorChannelCapabilities = {
|
||||
dmAllowFromMode: "topOnly",
|
||||
groupModel: "sender",
|
||||
groupAllowFromFallbackToAllowFrom: true,
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
};
|
||||
|
||||
const DOCTOR_CHANNEL_CAPABILITIES: Record<string, DoctorChannelCapabilities> = {
|
||||
discord: {
|
||||
dmAllowFromMode: "topOrNested",
|
||||
groupModel: "route",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: false,
|
||||
},
|
||||
googlechat: {
|
||||
dmAllowFromMode: "nestedOnly",
|
||||
groupModel: "route",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: false,
|
||||
},
|
||||
imessage: {
|
||||
dmAllowFromMode: "topOnly",
|
||||
groupModel: "sender",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
},
|
||||
irc: {
|
||||
dmAllowFromMode: "topOnly",
|
||||
groupModel: "sender",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
},
|
||||
matrix: {
|
||||
dmAllowFromMode: "nestedOnly",
|
||||
groupModel: "sender",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
},
|
||||
msteams: {
|
||||
dmAllowFromMode: "topOnly",
|
||||
groupModel: "hybrid",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
},
|
||||
slack: {
|
||||
dmAllowFromMode: "topOrNested",
|
||||
groupModel: "route",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: false,
|
||||
},
|
||||
zalouser: {
|
||||
dmAllowFromMode: "topOnly",
|
||||
groupModel: "hybrid",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: false,
|
||||
},
|
||||
};
|
||||
|
||||
export function getDoctorChannelCapabilities(channelName?: string): DoctorChannelCapabilities {
|
||||
if (!channelName) {
|
||||
return DEFAULT_DOCTOR_CHANNEL_CAPABILITIES;
|
||||
}
|
||||
return DOCTOR_CHANNEL_CAPABILITIES[channelName] ?? DEFAULT_DOCTOR_CHANNEL_CAPABILITIES;
|
||||
}
|
||||
|
|
@ -1,11 +1,7 @@
|
|||
import { getDoctorChannelCapabilities } from "../channel-capabilities.js";
|
||||
|
||||
export type AllowFromMode = "topOnly" | "topOrNested" | "nestedOnly";
|
||||
|
||||
export function resolveAllowFromMode(channelName: string): AllowFromMode {
|
||||
if (channelName === "googlechat" || channelName === "matrix") {
|
||||
return "nestedOnly";
|
||||
}
|
||||
if (channelName === "discord" || channelName === "slack") {
|
||||
return "topOrNested";
|
||||
}
|
||||
return "topOnly";
|
||||
return getDoctorChannelCapabilities(channelName).dmAllowFromMode;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { getDoctorChannelCapabilities } from "../channel-capabilities.js";
|
||||
import type { DoctorAccountRecord, DoctorAllowFromList } from "../types.js";
|
||||
import { hasAllowFromEntries } from "./allowlist.js";
|
||||
|
||||
|
|
@ -10,31 +11,11 @@ type CollectEmptyAllowlistPolicyWarningsParams = {
|
|||
};
|
||||
|
||||
function usesSenderBasedGroupAllowlist(channelName?: string): boolean {
|
||||
if (!channelName) {
|
||||
return true;
|
||||
}
|
||||
// These channels enforce group access via channel/space config, not sender-based
|
||||
// groupAllowFrom lists.
|
||||
return !(
|
||||
channelName === "discord" ||
|
||||
channelName === "slack" ||
|
||||
channelName === "googlechat" ||
|
||||
channelName === "zalouser"
|
||||
);
|
||||
return getDoctorChannelCapabilities(channelName).warnOnEmptyGroupSenderAllowlist;
|
||||
}
|
||||
|
||||
function allowsGroupAllowFromFallback(channelName?: string): boolean {
|
||||
if (!channelName) {
|
||||
return true;
|
||||
}
|
||||
// Keep doctor warnings aligned with runtime access semantics.
|
||||
return !(
|
||||
channelName === "googlechat" ||
|
||||
channelName === "imessage" ||
|
||||
channelName === "matrix" ||
|
||||
channelName === "msteams" ||
|
||||
channelName === "irc"
|
||||
);
|
||||
return getDoctorChannelCapabilities(channelName).groupAllowFromFallbackToAllowFrom;
|
||||
}
|
||||
|
||||
export function collectEmptyAllowlistPolicyWarningsForAccount(
|
||||
|
|
|
|||
Loading…
Reference in New Issue