From 0cc4d42f3f4a2e7bfdee8ac5e23ef7170620ff93 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 17 Aug 2026 11:49:26 -0700 Subject: [PATCH] fix(discord): accept shipped nested DM access config (#125359) Amp-Thread-ID: https://ampcode.com/threads/T-01a00a6a-b64e-74a5-8b15-2d3b966a468d Co-authored-by: Amp --- extensions/discord/src/config-schema.test.ts | 37 +++++++++++++++++--- extensions/discord/src/config-schema.ts | 36 +++++++++++++++++-- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/extensions/discord/src/config-schema.test.ts b/extensions/discord/src/config-schema.test.ts index 04957ce8fde2..32dad5073b5d 100644 --- a/extensions/discord/src/config-schema.test.ts +++ b/extensions/discord/src/config-schema.test.ts @@ -75,12 +75,41 @@ describe("discord config schema", () => { expectInvalidDiscordConfig({ mentionAliases: { opslead: "not-a-user-id" } }); }); - it("rejects legacy nested DM access keys", () => { - const issues = expectInvalidDiscordConfig({ - dm: { policy: "open", allowFrom: [] }, + it("normalizes shipped nested DM access keys at root and account scope", () => { + const cfg = expectValidDiscordConfig({ + dmPolicy: "pairing", + allowFrom: ["canonical-root"], + dm: { enabled: false, policy: "open", allowFrom: ["legacy-root"] }, + accounts: { + work: { + dmPolicy: "allowlist", + allowFrom: ["canonical-account"], + dm: { groupEnabled: true, policy: "disabled", allowFrom: ["legacy-account"] }, + }, + personal: { + dm: { enabled: true, policy: "open", allowFrom: ["*"] }, + }, + }, }); - expect(issues[0]?.path.join(".")).toBe("dm"); + expect(cfg).toMatchObject({ + dmPolicy: "pairing", + allowFrom: ["canonical-root"], + dm: { enabled: false }, + accounts: { + work: { + dmPolicy: "allowlist", + allowFrom: ["canonical-account"], + dm: { groupEnabled: true }, + }, + personal: { + dmPolicy: "open", + allowFrom: ["*"], + dm: { enabled: true }, + }, + }, + }); + expectInvalidDiscordConfig({ dm: { enabled: false, unexpected: true } }); }); it("accepts textChunkLimit without reviving legacy message limits", () => { diff --git a/extensions/discord/src/config-schema.ts b/extensions/discord/src/config-schema.ts index 51e35ce23d8d..6e04999b678b 100644 --- a/extensions/discord/src/config-schema.ts +++ b/extensions/discord/src/config-schema.ts @@ -1,3 +1,4 @@ +import { normalizeLegacyDmAliases } from "openclaw/plugin-sdk/channel-config-helpers"; // Discord helper module supports config schema behavior. import { buildChannelAllowBotsSchema, @@ -15,6 +16,7 @@ import { requireOpenAllowFrom, TtsConfigSchema, } from "openclaw/plugin-sdk/channel-config-schema"; +import { asObjectRecord } from "openclaw/plugin-sdk/runtime-doctor-migrations"; import { buildSecretInputSchema, registerSensitiveConfigSchema, @@ -196,7 +198,7 @@ const DiscordVoiceSchema = z .strict() .optional(); -const DiscordAccountSchema = z +const DiscordAccountSchemaBase = z .object({ ...buildCommonChannelAccountShape({ omit: ["groupAllowFrom"], @@ -372,7 +374,32 @@ const DiscordAccountSchema = z // can inherit top-level allowFrom via runtime shallow merge. }); -export const DiscordConfigSchema = DiscordAccountSchema.extend({ +function normalizeShippedDiscordDmAliases(value: unknown): unknown { + const entry = asObjectRecord(value); + if (!entry) { + return value; + } + + const updated = normalizeLegacyDmAliases({ + entry, + pathPrefix: "channels.discord", + changes: [], + }).entry; + const dm = asObjectRecord(updated.dm); + if (!dm || (dm.policy === undefined && dm.allowFrom === undefined)) { + return updated; + } + const { policy: _policy, allowFrom: _allowFrom, ...retainedDm } = dm; + const { dm: _dm, ...rest } = updated; + return Object.keys(retainedDm).length > 0 ? { ...rest, dm: retainedDm } : rest; +} + +const DiscordAccountSchema = z.preprocess( + normalizeShippedDiscordDmAliases, + DiscordAccountSchemaBase, +); + +const DiscordConfigSchemaBase = DiscordAccountSchemaBase.extend({ accounts: z.record(z.string(), DiscordAccountSchema.optional()).optional(), defaultAccount: z.string().optional(), }).superRefine((value, ctx) => { @@ -422,6 +449,11 @@ export const DiscordConfigSchema = DiscordAccountSchema.extend({ } }); +export const DiscordConfigSchema = z.preprocess( + normalizeShippedDiscordDmAliases, + DiscordConfigSchemaBase, +); + export const DiscordChannelConfigSchema = buildChannelConfigSchema(DiscordConfigSchema, { uiHints: discordChannelConfigUiHints, });