feat(queue): add per-channel debounce overrides
This commit is contained in:
@@ -11,6 +11,7 @@ Docs: https://docs.clawd.bot
|
|||||||
- Nodes: expose node PATH in status/describe and bootstrap PATH for node-host execution.
|
- 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: 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.
|
- 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
|
### Fixes
|
||||||
- Embedded runner: persist injected history images so attachments aren’t reloaded each turn. (#1374) Thanks @Nicell.
|
- Embedded runner: persist injected history images so attachments aren’t reloaded each turn. (#1374) Thanks @Nicell.
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ const resolveChannelOverride = (params: {
|
|||||||
channel: string;
|
channel: string;
|
||||||
}): number | undefined => {
|
}): number | undefined => {
|
||||||
if (!params.byChannel) return undefined;
|
if (!params.byChannel) return undefined;
|
||||||
const channelKey = params.channel as keyof InboundDebounceByProvider;
|
return resolveMs(params.byChannel[params.channel]);
|
||||||
return resolveMs(params.byChannel[channelKey]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function resolveInboundDebounceMs(params: {
|
export function resolveInboundDebounceMs(params: {
|
||||||
|
|||||||
@@ -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 { normalizeQueueDropPolicy, normalizeQueueMode } from "./normalize.js";
|
||||||
import { DEFAULT_QUEUE_CAP, DEFAULT_QUEUE_DEBOUNCE_MS, DEFAULT_QUEUE_DROP } from "./state.js";
|
import { DEFAULT_QUEUE_CAP, DEFAULT_QUEUE_DEBOUNCE_MS, DEFAULT_QUEUE_DROP } from "./state.js";
|
||||||
import type { QueueMode, QueueSettings, ResolveQueueSettingsParams } from "./types.js";
|
import type { QueueMode, QueueSettings, ResolveQueueSettingsParams } from "./types.js";
|
||||||
@@ -6,6 +8,23 @@ function defaultQueueModeForChannel(_channel?: string): QueueMode {
|
|||||||
return "collect";
|
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 {
|
export function resolveQueueSettings(params: ResolveQueueSettingsParams): QueueSettings {
|
||||||
const channelKey = params.channel?.trim().toLowerCase();
|
const channelKey = params.channel?.trim().toLowerCase();
|
||||||
const queueCfg = params.cfg.messages?.queue;
|
const queueCfg = params.cfg.messages?.queue;
|
||||||
@@ -22,6 +41,8 @@ export function resolveQueueSettings(params: ResolveQueueSettingsParams): QueueS
|
|||||||
const debounceRaw =
|
const debounceRaw =
|
||||||
params.inlineOptions?.debounceMs ??
|
params.inlineOptions?.debounceMs ??
|
||||||
params.sessionEntry?.queueDebounceMs ??
|
params.sessionEntry?.queueDebounceMs ??
|
||||||
|
resolveChannelDebounce(queueCfg?.debounceMsByChannel, channelKey) ??
|
||||||
|
resolvePluginDebounce(channelKey) ??
|
||||||
queueCfg?.debounceMs ??
|
queueCfg?.debounceMs ??
|
||||||
DEFAULT_QUEUE_DEBOUNCE_MS;
|
DEFAULT_QUEUE_DEBOUNCE_MS;
|
||||||
const capRaw =
|
const capRaw =
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ export type ChannelPlugin<ResolvedAccount = any> = {
|
|||||||
id: ChannelId;
|
id: ChannelId;
|
||||||
meta: ChannelMeta;
|
meta: ChannelMeta;
|
||||||
capabilities: ChannelCapabilities;
|
capabilities: ChannelCapabilities;
|
||||||
|
defaults?: {
|
||||||
|
queue?: {
|
||||||
|
debounceMs?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
reload?: { configPrefixes: string[]; noopPrefixes?: string[] };
|
reload?: { configPrefixes: string[]; noopPrefixes?: string[] };
|
||||||
// CLI onboarding wizard hooks for this channel.
|
// CLI onboarding wizard hooks for this channel.
|
||||||
onboarding?: ChannelOnboardingAdapter;
|
onboarding?: ChannelOnboardingAdapter;
|
||||||
|
|||||||
@@ -13,20 +13,13 @@ export type QueueConfig = {
|
|||||||
mode?: QueueMode;
|
mode?: QueueMode;
|
||||||
byChannel?: QueueModeByProvider;
|
byChannel?: QueueModeByProvider;
|
||||||
debounceMs?: number;
|
debounceMs?: number;
|
||||||
|
/** Per-channel debounce overrides (ms). */
|
||||||
|
debounceMsByChannel?: InboundDebounceByProvider;
|
||||||
cap?: number;
|
cap?: number;
|
||||||
drop?: QueueDropPolicy;
|
drop?: QueueDropPolicy;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InboundDebounceByProvider = {
|
export type InboundDebounceByProvider = Record<string, number>;
|
||||||
whatsapp?: number;
|
|
||||||
telegram?: number;
|
|
||||||
discord?: number;
|
|
||||||
slack?: number;
|
|
||||||
signal?: number;
|
|
||||||
imessage?: number;
|
|
||||||
msteams?: number;
|
|
||||||
webchat?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InboundDebounceConfig = {
|
export type InboundDebounceConfig = {
|
||||||
debounceMs?: number;
|
debounceMs?: number;
|
||||||
|
|||||||
@@ -217,17 +217,7 @@ export const QueueModeBySurfaceSchema = z
|
|||||||
.optional();
|
.optional();
|
||||||
|
|
||||||
export const DebounceMsBySurfaceSchema = z
|
export const DebounceMsBySurfaceSchema = z
|
||||||
.object({
|
.record(z.string(), z.number().int().nonnegative())
|
||||||
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()
|
|
||||||
.optional();
|
.optional();
|
||||||
|
|
||||||
export const QueueSchema = z
|
export const QueueSchema = z
|
||||||
@@ -235,6 +225,7 @@ export const QueueSchema = z
|
|||||||
mode: QueueModeSchema.optional(),
|
mode: QueueModeSchema.optional(),
|
||||||
byChannel: QueueModeBySurfaceSchema,
|
byChannel: QueueModeBySurfaceSchema,
|
||||||
debounceMs: z.number().int().nonnegative().optional(),
|
debounceMs: z.number().int().nonnegative().optional(),
|
||||||
|
debounceMsByChannel: DebounceMsBySurfaceSchema,
|
||||||
cap: z.number().int().positive().optional(),
|
cap: z.number().int().positive().optional(),
|
||||||
drop: QueueDropSchema.optional(),
|
drop: QueueDropSchema.optional(),
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user