fix(doctor): explain discord streaming opt-in (#52450)

This commit is contained in:
Vincent Koc
2026-03-22 12:37:03 -07:00
committed by GitHub
parent c26655d397
commit 5f723ecd7f
7 changed files with 47 additions and 1 deletions

View File

@@ -582,6 +582,7 @@ Default slash command settings:
OpenClaw can stream draft replies by sending a temporary message and editing it as text arrives.
- `channels.discord.streaming` controls preview streaming (`off` | `partial` | `block` | `progress`, default: `off`).
- Default stays `off` because Discord preview edits can hit rate limits quickly, especially when multiple bots or gateways share the same account or guild traffic.
- `progress` is accepted for cross-channel consistency and maps to `partial` on Discord.
- `channels.discord.streamMode` is a legacy alias and is auto-migrated.
- `partial` edits a single preview message as tokens arrive.

View File

@@ -179,7 +179,7 @@ WhatsApp runs through the gateway's web channel (Baileys Web). It starts automat
historyLimit: 50,
replyToMode: "first", // off | first | all
linkPreview: true,
streaming: "partial", // off | partial | block | progress (default: off)
streaming: "partial", // off | partial | block | progress (default: off; opt in explicitly to avoid preview-edit rate limits)
actions: { reactions: true, sendMessage: true },
reactionNotifications: "own", // off | own | all
mediaMaxMb: 100,

View File

@@ -537,6 +537,12 @@ describe("processDiscordMessage draft streaming", () => {
expectSinglePreviewEdit();
});
it("keeps preview streaming off by default when streaming is unset", async () => {
await runSingleChunkFinalScenario({ maxLinesPerMessage: 5 });
expect(editMessageDiscord).not.toHaveBeenCalled();
expect(deliverDiscordReply).toHaveBeenCalledTimes(1);
});
it("falls back to standard send when final needs multiple chunks", async () => {
await runSingleChunkFinalScenario({ streamMode: "partial", maxLinesPerMessage: 1 });

View File

@@ -32,6 +32,23 @@ describe("normalizeCompatibilityConfigValues preview streaming aliases", () => {
]);
});
it("explains why discord preview streaming stays off when legacy config resolves to off", () => {
const res = normalizeCompatibilityConfigValues({
channels: {
discord: {
streamMode: "off",
},
},
});
expect(res.config.channels?.discord?.streaming).toBe("off");
expect(res.config.channels?.discord?.streamMode).toBeUndefined();
expect(res.changes).toEqual([
"Moved channels.discord.streamMode → channels.discord.streaming (off).",
'channels.discord.streaming remains off by default to avoid Discord preview-edit rate limits; set channels.discord.streaming="partial" to opt in explicitly.',
]);
});
it("normalizes slack boolean streaming aliases to enum and native streaming", () => {
const res = normalizeCompatibilityConfigValues({
channels: {

View File

@@ -142,6 +142,11 @@ export function normalizeCompatibilityConfigValues(cfg: OpenClawConfig): {
`Normalized ${params.pathPrefix}.streaming (${beforeStreaming}) → (${resolved}).`,
);
}
if (params.pathPrefix.startsWith("channels.discord") && resolved === "off") {
changes.push(
`${params.pathPrefix}.streaming remains off by default to avoid Discord preview-edit rate limits; set ${params.pathPrefix}.streaming="partial" to opt in explicitly.`,
);
}
return { entry: updated, changed };
};

View File

@@ -0,0 +1,14 @@
import { describe, expect, it } from "vitest";
import { resolveDiscordPreviewStreamMode } from "./discord-preview-streaming.js";
describe("resolveDiscordPreviewStreamMode", () => {
it("defaults to off when unset", () => {
expect(resolveDiscordPreviewStreamMode({})).toBe("off");
});
it("preserves explicit off", () => {
expect(resolveDiscordPreviewStreamMode({ streaming: "off" })).toBe("off");
expect(resolveDiscordPreviewStreamMode({ streamMode: "off" })).toBe("off");
expect(resolveDiscordPreviewStreamMode({ streaming: false })).toBe("off");
});
});

View File

@@ -104,6 +104,9 @@ export function resolveDiscordPreviewStreamMode(
if (typeof params.streaming === "boolean") {
return params.streaming ? "partial" : "off";
}
// Discord preview streaming edits can hit aggressive rate limits, especially
// when multiple gateways or multiple bots share the same account/server. Keep
// the default off unless the operator opts in explicitly.
return "off";
}