mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(auto-reply): never allow silent replies for mentioned group turns
The default group silentReply policy sanctions silence for ambient chatter, which also swallowed empty completions on turns that explicitly mentioned the bot - the reported production failure. A mention is a directed turn: treat it like a direct chat and let the no-visible-reply fallback fire. Proven live: group mention + empty completion delivers exactly one fallback message; ambient group turns stay silent; normal replies produce no fallback.
This commit is contained in:
@@ -425,6 +425,56 @@ describe("sendPolicy deny — suppress delivery, not processing (#53328)", () =>
|
||||
expect(result.noVisibleReplyFallbackEligible).toBeUndefined();
|
||||
});
|
||||
|
||||
it("delivers fallback for mentioned group turns even when group silence is allowed", async () => {
|
||||
setNoAbort();
|
||||
const dispatcher = createDispatcher();
|
||||
const replyResolver = vi.fn(async () => undefined);
|
||||
const ctx = buildTestCtx({
|
||||
ChatType: "group",
|
||||
Surface: "telegram",
|
||||
Provider: "telegram",
|
||||
SessionKey: "agent:main:telegram:group:oc_group",
|
||||
WasMentioned: true,
|
||||
});
|
||||
|
||||
// No silentReply config: group default is "allow", but an explicit mention
|
||||
// is a directed turn and must never end silently.
|
||||
const result = await dispatchReplyFromConfig({
|
||||
ctx,
|
||||
cfg: emptyConfig,
|
||||
dispatcher,
|
||||
replyResolver,
|
||||
});
|
||||
|
||||
expect(dispatcher.sendFinalReply).toHaveBeenCalledWith({
|
||||
text: NO_VISIBLE_REPLY_FALLBACK_TEXT,
|
||||
});
|
||||
expect(result.noVisibleReplyFallbackDelivered).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps ambient group turns silent under the default group policy", async () => {
|
||||
setNoAbort();
|
||||
const dispatcher = createDispatcher();
|
||||
const replyResolver = vi.fn(async () => undefined);
|
||||
const ctx = buildTestCtx({
|
||||
ChatType: "group",
|
||||
Surface: "telegram",
|
||||
Provider: "telegram",
|
||||
SessionKey: "agent:main:telegram:group:oc_group",
|
||||
});
|
||||
|
||||
const result = await dispatchReplyFromConfig({
|
||||
ctx,
|
||||
cfg: emptyConfig,
|
||||
dispatcher,
|
||||
replyResolver,
|
||||
});
|
||||
|
||||
expect(dispatcher.sendFinalReply).not.toHaveBeenCalled();
|
||||
expect(result.noVisibleReplyFallbackDelivered).toBeUndefined();
|
||||
expect(result.noVisibleReplyFallbackEligible).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not deliver no-visible fallback when silentReply allows empty finals", async () => {
|
||||
setNoAbort();
|
||||
const dispatcher = createDispatcher();
|
||||
|
||||
@@ -224,7 +224,11 @@ export async function prepareDispatchOperationContext(state: PrepareDispatchDeli
|
||||
const chatType = normalizeChatType(ctx.ChatType);
|
||||
const silentReplyConversationType = resolveRoutedPolicyConversationType(ctx);
|
||||
const silentReplySurface = normalizeLowercaseStringOrEmpty(ctx.Surface ?? ctx.Provider);
|
||||
// Group silent-reply policy sanctions silence for ambient chatter only. A turn
|
||||
// that explicitly addressed the bot (mention) must never end silently, matching
|
||||
// the hard-coded direct-chat rule in resolveSilentReplyPolicyFromPolicies.
|
||||
const emptyFinalAllowedAsSilent =
|
||||
ctx.WasMentioned !== true &&
|
||||
silentReplyConversationType !== undefined &&
|
||||
resolveSilentReplyPolicyFromPolicies({
|
||||
conversationType: silentReplyConversationType,
|
||||
|
||||
Reference in New Issue
Block a user