diff --git a/CHANGELOG.md b/CHANGELOG.md index e9cb1c92e27..f2d0baf4557 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Plugins/catalog: merge official external catalog descriptors into partial package channel config metadata, so lagging WeCom/Yuanbao manifests keep their own schema while still exposing host-supplied labels and setup text. Thanks @vincentkoc. - Plugins/catalog: supplement lagging official external WeCom and Yuanbao npm manifests with channel config descriptors and declared tool contracts from the OpenClaw catalog, so trusted package sweeps no longer fail because external package metadata trails the host contract. Thanks @vincentkoc. - Plugins/install: let trusted official `@openclaw/*` catalog installs recover when npm `latest` points at a prerelease by falling back to the newest stable version, or by allowing prerelease-only launch packages with a warning instead of making beta/development plugin sweeps fail at install time. Thanks @vincentkoc. - Google Meet: grant Chrome media permissions against the actual Meet tab, start the local realtime audio bridge only after Meet joins, expose realtime transcripts in status/logs, and force explicit audio responses with current OpenAI realtime output-audio events so BlackHole capture does not keep the OpenClaw participant muted or silent. diff --git a/src/plugins/manifest-registry.test.ts b/src/plugins/manifest-registry.test.ts index 9abcc19dec5..ef0519da8ff 100644 --- a/src/plugins/manifest-registry.test.ts +++ b/src/plugins/manifest-registry.test.ts @@ -1133,6 +1133,48 @@ describe("loadPluginManifestRegistry", () => { ).toBe(false); }); + it("fills missing official external catalog descriptors for partial npm channel configs", () => { + const dir = makeTempDir(); + writeManifest(dir, { + id: "wecom-openclaw-plugin", + channels: ["wecom"], + configSchema: { type: "object" }, + channelConfigs: { + wecom: { + schema: { + type: "object", + additionalProperties: false, + properties: { + corpId: { type: "string" }, + }, + }, + }, + }, + }); + + const registry = loadRegistry([ + createPluginCandidate({ + idHint: "wecom-openclaw-plugin", + rootDir: dir, + origin: "global", + packageName: "@wecom/wecom-openclaw-plugin", + }), + ]); + + expect(registry.plugins[0]?.channelConfigs?.wecom).toEqual( + expect.objectContaining({ + label: "WeCom", + description: "Enterprise WeChat conversation channel.", + schema: expect.objectContaining({ + additionalProperties: false, + properties: { + corpId: { type: "string" }, + }, + }), + }), + ); + }); + it("drops prototype-polluting channel config keys from plugin manifests", () => { const dir = makeTempDir(); writeTextFile( diff --git a/src/plugins/manifest-registry.ts b/src/plugins/manifest-registry.ts index d0a6676a66a..1fb72bd82f5 100644 --- a/src/plugins/manifest-registry.ts +++ b/src/plugins/manifest-registry.ts @@ -322,7 +322,37 @@ function mergeCatalogChannelConfigs(params: { } for (const [key, value] of Object.entries(params.manifestChannelConfigs ?? {})) { if (!isBlockedObjectKey(key)) { - merged[key] = value; + const catalogValue = merged[key]; + merged[key] = catalogValue + ? { + ...catalogValue, + ...value, + schema: value.schema ?? catalogValue.schema, + ...(catalogValue.uiHints || value.uiHints + ? { + uiHints: { + ...catalogValue.uiHints, + ...value.uiHints, + }, + } + : {}), + ...((value.runtime ?? catalogValue.runtime) + ? { runtime: value.runtime ?? catalogValue.runtime } + : {}), + ...((value.label ?? catalogValue.label) + ? { label: value.label ?? catalogValue.label } + : {}), + ...((value.description ?? catalogValue.description) + ? { description: value.description ?? catalogValue.description } + : {}), + ...((value.preferOver ?? catalogValue.preferOver) + ? { preferOver: value.preferOver ?? catalogValue.preferOver } + : {}), + ...((value.commands ?? catalogValue.commands) + ? { commands: value.commands ?? catalogValue.commands } + : {}), + } + : value; } } return Object.keys(merged).length > 0 ? merged : undefined;