feat(queue): add per-channel debounce overrides

This commit is contained in:
Peter Steinberger
2026-01-21 18:36:10 +00:00
parent 6996c0f330
commit 32550154f9
6 changed files with 33 additions and 23 deletions

View File

@@ -11,6 +11,7 @@ Docs: https://docs.clawd.bot
- Nodes: expose node PATH in status/describe and bootstrap PATH for node-host execution.
- CLI: flatten node service commands under `clawdbot node` and remove `service node` docs.
- CLI: move gateway service commands under `clawdbot gateway` and add `gateway probe` for reachability.
- Queue: allow per-channel debounce overrides and plugin defaults. (#1190) Thanks @cheeeee.
### Fixes
- Embedded runner: persist injected history images so attachments arent reloaded each turn. (#1374) Thanks @Nicell.

View File

@@ -11,8 +11,7 @@ const resolveChannelOverride = (params: {
channel: string;
}): number | undefined => {
if (!params.byChannel) return undefined;
const channelKey = params.channel as keyof InboundDebounceByProvider;
return resolveMs(params.byChannel[channelKey]);
return resolveMs(params.byChannel[params.channel]);
};
export function resolveInboundDebounceMs(params: {

View File

@@ -1,3 +1,5 @@
import { getChannelPlugin } from "../../../channels/plugins/index.js";
import type { InboundDebounceByProvider } from "../../../config/types.messages.js";
import { normalizeQueueDropPolicy, normalizeQueueMode } from "./normalize.js";
import { DEFAULT_QUEUE_CAP, DEFAULT_QUEUE_DEBOUNCE_MS, DEFAULT_QUEUE_DROP } from "./state.js";
import type { QueueMode, QueueSettings, ResolveQueueSettingsParams } from "./types.js";
@@ -6,6 +8,23 @@ function defaultQueueModeForChannel(_channel?: string): QueueMode {
return "collect";
}
/** Resolve per-channel debounce override from debounceMsByChannel map. */
function resolveChannelDebounce(
byChannel: InboundDebounceByProvider | undefined,
channelKey: string | undefined,
): number | undefined {
if (!channelKey || !byChannel) return undefined;
const value = byChannel[channelKey];
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : undefined;
}
function resolvePluginDebounce(channelKey: string | undefined): number | undefined {
if (!channelKey) return undefined;
const plugin = getChannelPlugin(channelKey);
const value = plugin?.defaults?.queue?.debounceMs;
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : undefined;
}
export function resolveQueueSettings(params: ResolveQueueSettingsParams): QueueSettings {
const channelKey = params.channel?.trim().toLowerCase();
const queueCfg = params.cfg.messages?.queue;
@@ -22,6 +41,8 @@ export function resolveQueueSettings(params: ResolveQueueSettingsParams): QueueS
const debounceRaw =
params.inlineOptions?.debounceMs ??
params.sessionEntry?.queueDebounceMs ??
resolveChannelDebounce(queueCfg?.debounceMsByChannel, channelKey) ??
resolvePluginDebounce(channelKey) ??
queueCfg?.debounceMs ??
DEFAULT_QUEUE_DEBOUNCE_MS;
const capRaw =

View File

@@ -48,6 +48,11 @@ export type ChannelPlugin<ResolvedAccount = any> = {
id: ChannelId;
meta: ChannelMeta;
capabilities: ChannelCapabilities;
defaults?: {
queue?: {
debounceMs?: number;
};
};
reload?: { configPrefixes: string[]; noopPrefixes?: string[] };
// CLI onboarding wizard hooks for this channel.
onboarding?: ChannelOnboardingAdapter;

View File

@@ -13,20 +13,13 @@ export type QueueConfig = {
mode?: QueueMode;
byChannel?: QueueModeByProvider;
debounceMs?: number;
/** Per-channel debounce overrides (ms). */
debounceMsByChannel?: InboundDebounceByProvider;
cap?: number;
drop?: QueueDropPolicy;
};
export type InboundDebounceByProvider = {
whatsapp?: number;
telegram?: number;
discord?: number;
slack?: number;
signal?: number;
imessage?: number;
msteams?: number;
webchat?: number;
};
export type InboundDebounceByProvider = Record<string, number>;
export type InboundDebounceConfig = {
debounceMs?: number;

View File

@@ -217,17 +217,7 @@ export const QueueModeBySurfaceSchema = z
.optional();
export const DebounceMsBySurfaceSchema = z
.object({
whatsapp: z.number().int().nonnegative().optional(),
telegram: z.number().int().nonnegative().optional(),
discord: z.number().int().nonnegative().optional(),
slack: z.number().int().nonnegative().optional(),
signal: z.number().int().nonnegative().optional(),
imessage: z.number().int().nonnegative().optional(),
msteams: z.number().int().nonnegative().optional(),
webchat: z.number().int().nonnegative().optional(),
})
.strict()
.record(z.string(), z.number().int().nonnegative())
.optional();
export const QueueSchema = z
@@ -235,6 +225,7 @@ export const QueueSchema = z
mode: QueueModeSchema.optional(),
byChannel: QueueModeBySurfaceSchema,
debounceMs: z.number().int().nonnegative().optional(),
debounceMsByChannel: DebounceMsBySurfaceSchema,
cap: z.number().int().positive().optional(),
drop: QueueDropSchema.optional(),
})