fix(plugins): dedupe manifest diagnostics

This commit is contained in:
Vincent Koc
2026-05-03 13:51:14 -07:00
parent 9ebdd26020
commit 5355ef0f08
3 changed files with 46 additions and 1 deletions

View File

@@ -74,6 +74,7 @@ Docs: https://docs.openclaw.ai
- Doctor/Telegram: warn when selected Telegram quote replies can suppress `streaming.preview.toolProgress`, and document the `replyToMode` trade-off without changing runtime delivery. Fixes #73487. Thanks @GodsBoy.
- Channels/Discord: send a best-effort native typing cue immediately after an inbound DM is accepted, so slow pre-dispatch turns show Discord liveness before queueing, context assembly, model, or tool work starts. Fixes #76417. Thanks @mlopez14.
- Plugins/install: reject source-only TypeScript package installs and installed plugin packages that are missing compiled runtime output, so broken npm artifacts fail at install/discovery time instead of falling through jiti and surfacing later as unavailable providers. Fixes #76720.
- Plugins/config: deduplicate identical manifest compatibility diagnostics when an explicitly configured plugin overrides another discovered candidate, so external channel plugins do not print the same missing `channelConfigs` warning repeatedly during install and enable. Thanks @vincentkoc.
- Discord/status: honor explicit `messages.statusReactions.enabled: true` in tool-only guild channels so queued ack reactions can progress through thinking/done lifecycle reactions instead of stopping at the initial emoji. Thanks @Marvinthebored.
- Discord/native commands: compare Discord-normalized slash-command descriptions and localized descriptions during reconcile so CJK or multiline command text no longer triggers redundant startup PATCH bursts and rate-limit 429s. Fixes #76587. Thanks @zhengsx.
- Agents/OpenAI: omit Chat Completions `reasoning_effort` for `gpt-5.4-mini` only when function tools are present while preserving tool-free Chat and Responses reasoning support, preventing Telegram-routed fallback runs from hanging after OpenAI rejects tool payloads. Fixes #76176. Thanks @ThisIsAdilah and @chinar-amrutkar.

View File

@@ -400,6 +400,36 @@ describe("loadPluginManifestRegistry", () => {
expect(warning?.message).toContain(path.join(configDir, "index.ts"));
});
it("deduplicates compatibility diagnostics when a config plugin replaces a global candidate", () => {
const globalDir = makeTempDir();
const configDir = makeTempDir();
const manifest = {
id: "external-chat",
channels: ["external-chat"],
configSchema: { type: "object" },
};
writeManifest(globalDir, manifest);
writeManifest(configDir, manifest);
const registry = loadRegistry([
createPluginCandidate({
idHint: "external-chat",
rootDir: globalDir,
origin: "global",
}),
createPluginCandidate({
idHint: "external-chat",
rootDir: configDir,
origin: "config",
}),
]);
const channelConfigWarnings = registry.diagnostics.filter((diagnostic) =>
diagnostic.message.includes("without channelConfigs metadata"),
);
expect(channelConfigWarnings).toHaveLength(1);
});
it("suppresses duplicate warnings for explicit installed globals overriding bundled plugins", () => {
const bundledDir = makeTempDir();
const globalDir = makeTempDir();

View File

@@ -490,6 +490,20 @@ function pushManifestCompatibilityDiagnostics(params: {
pushNonBundledChannelConfigDescriptorDiagnostic(params);
}
function dedupePluginDiagnostics(diagnostics: PluginDiagnostic[]): PluginDiagnostic[] {
const seen = new Set<string>();
const deduped: PluginDiagnostic[] = [];
for (const diagnostic of diagnostics) {
const key = JSON.stringify([diagnostic.level, diagnostic.pluginId ?? "", diagnostic.message]);
if (seen.has(key)) {
continue;
}
seen.add(key);
deduped.push(diagnostic);
}
return deduped;
}
function matchesInstalledPluginRecord(params: {
pluginId: string;
candidate: PluginCandidate;
@@ -786,6 +800,6 @@ export function loadPluginManifestRegistry(
pushManifestCompatibilityDiagnostics({ record, diagnostics });
}
const registry = { plugins: records, diagnostics };
const registry = { plugins: records, diagnostics: dedupePluginDiagnostics(diagnostics) };
return registry;
}