mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-03 07:24:04 +00:00
fix(channels): bypass debounce for bare abort triggers [AI-assisted] (#83348)
Summary: - The PR changes shared, Feishu, Mattermost, Microsoft Teams, and WhatsApp inbound debounce predicates so bare abort text bypasses debounce, then adds focused tests and a changelog entry. - Reproducibility: yes. source-level. Current main sends bare `stop`, `abort`, and `wait` through a `hasContro ... ()` debounce gate, while the existing abort-aware detector and trigger set already recognize those phrases. Automerge notes: - PR branch already contained follow-up commit before automerge: fix(channels): bypass debounce for bare abort triggers [AI-assisted] - PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8334… Validation: - ClawSweeper review passed for headc96bf84270. - Required merge gates passed before the squash merge. Prepared head SHA:c96bf84270Review: https://github.com/openclaw/openclaw/pull/83348#issuecomment-4473176095 Co-authored-by: IWhatsskill <284122573+IWhatsskill@users.noreply.github.com> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com> Approved-by: takhoffman Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -262,7 +262,7 @@ export function createFeishuMessageReceiveHandler({
|
||||
return false;
|
||||
}
|
||||
const text = resolveDebounceText(event);
|
||||
return Boolean(text) && !core.channel.text.hasControlCommand(text, cfg);
|
||||
return Boolean(text) && !core.channel.commands.isControlCommandMessage(text, cfg);
|
||||
},
|
||||
onFlush: async (entries) => {
|
||||
const last = entries.at(-1);
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
createInboundDebouncer,
|
||||
resolveInboundDebounceMs,
|
||||
} from "openclaw/plugin-sdk/channel-inbound-debounce";
|
||||
import { hasControlCommand } from "openclaw/plugin-sdk/command-detection";
|
||||
import { hasControlCommand, isControlCommandMessage } from "openclaw/plugin-sdk/command-detection";
|
||||
import { createNonExitingRuntimeEnv } from "openclaw/plugin-sdk/plugin-test-runtime";
|
||||
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ClawdbotConfig, PluginRuntime } from "../runtime-api.js";
|
||||
@@ -255,10 +255,14 @@ function mentionOpenIds(event: FeishuMessageEvent): string[] {
|
||||
function createFeishuMonitorRuntime(params?: {
|
||||
createInboundDebouncer?: PluginRuntime["channel"]["debounce"]["createInboundDebouncer"];
|
||||
resolveInboundDebounceMs?: PluginRuntime["channel"]["debounce"]["resolveInboundDebounceMs"];
|
||||
isControlCommandMessage?: PluginRuntime["channel"]["commands"]["isControlCommandMessage"];
|
||||
hasControlCommand?: PluginRuntime["channel"]["text"]["hasControlCommand"];
|
||||
}): PluginRuntime {
|
||||
return {
|
||||
channel: {
|
||||
commands: {
|
||||
isControlCommandMessage: params?.isControlCommandMessage ?? isControlCommandMessage,
|
||||
},
|
||||
debounce: {
|
||||
createInboundDebouncer: params?.createInboundDebouncer ?? createInboundDebouncer,
|
||||
resolveInboundDebounceMs: params?.resolveInboundDebounceMs ?? resolveInboundDebounceMs,
|
||||
@@ -533,6 +537,32 @@ describe("Feishu inbound debounce regressions", () => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("releases pending text before a bare abort trigger instead of debouncing it", async () => {
|
||||
setDedupPassThroughMocks();
|
||||
const onMessage = await setupDebounceMonitor();
|
||||
|
||||
await enqueueDebouncedMessage(onMessage, createTextEvent({ messageId: "om_1", text: "first" }));
|
||||
expect(handleFeishuMessageMock).not.toHaveBeenCalled();
|
||||
|
||||
await enqueueDebouncedMessage(
|
||||
onMessage,
|
||||
createTextEvent({ messageId: "om_stop", text: "stop" }),
|
||||
);
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
|
||||
expect(handleFeishuMessageMock).toHaveBeenCalledTimes(2);
|
||||
const first = getFirstDispatchedEvent();
|
||||
const secondCall = mockCallAt(handleFeishuMessageMock, 1, "Feishu stop dispatch")[0] as
|
||||
| { event?: FeishuMessageEvent }
|
||||
| undefined;
|
||||
const second = secondCall?.event;
|
||||
expect(JSON.parse(first.message.content)).toEqual({ text: "first" });
|
||||
expect(second?.message.message_id).toBe("om_stop");
|
||||
expect(JSON.parse(second?.message.content ?? "{}")).toEqual({ text: "stop" });
|
||||
});
|
||||
|
||||
it("keeps bot mention when per-message mention keys collide across non-forward messages", async () => {
|
||||
setDedupPassThroughMocks();
|
||||
const onMessage = await setupDebounceMonitor();
|
||||
|
||||
Reference in New Issue
Block a user