Skip to content

Commit f66800b

Browse files
committed
Suppress internal source-reply final statuses
1 parent 00d2452 commit f66800b

2 files changed

Lines changed: 121 additions & 0 deletions

File tree

src/auto-reply/reply/dispatch-from-config.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7307,6 +7307,81 @@ describe("dispatchReplyFromConfig", () => {
73077307
expect((finalCalls[0]?.[0] as ReplyPayload | undefined)?.text).toBe("The answer is 42");
73087308
});
73097309

7310+
it("suppresses short internal final status text after a group source reply was delivered", async () => {
7311+
setNoAbort();
7312+
const dispatcher = createDispatcher();
7313+
const hasRepliedRef = { value: true };
7314+
const ctx = buildTestCtx({ Provider: "slack", Surface: "slack", ChatType: "group" });
7315+
const replyResolver = async () => ({ text: "Sent." }) satisfies ReplyPayload;
7316+
7317+
await dispatchReplyFromConfig({
7318+
ctx,
7319+
cfg: emptyConfig,
7320+
dispatcher,
7321+
replyResolver,
7322+
replyOptions: { hasRepliedRef },
7323+
});
7324+
7325+
expect(dispatcher.sendFinalReply).not.toHaveBeenCalled();
7326+
});
7327+
7328+
it("suppresses done-posted internal final status text after a group source reply was delivered", async () => {
7329+
setNoAbort();
7330+
const dispatcher = createDispatcher();
7331+
const hasRepliedRef = { value: true };
7332+
const ctx = buildTestCtx({ Provider: "slack", Surface: "slack", ChatType: "group" });
7333+
const replyResolver = async () =>
7334+
({ text: "Done - posted to #research-tasks." }) satisfies ReplyPayload;
7335+
7336+
await dispatchReplyFromConfig({
7337+
ctx,
7338+
cfg: emptyConfig,
7339+
dispatcher,
7340+
replyResolver,
7341+
replyOptions: { hasRepliedRef },
7342+
});
7343+
7344+
expect(dispatcher.sendFinalReply).not.toHaveBeenCalled();
7345+
});
7346+
7347+
it("keeps substantive group final text even when a source reply was delivered", async () => {
7348+
setNoAbort();
7349+
const dispatcher = createDispatcher();
7350+
const hasRepliedRef = { value: true };
7351+
const ctx = buildTestCtx({ Provider: "slack", Surface: "slack", ChatType: "group" });
7352+
const reply = { text: "Here is the actual answer." } satisfies ReplyPayload;
7353+
const replyResolver = async () => reply;
7354+
7355+
await dispatchReplyFromConfig({
7356+
ctx,
7357+
cfg: emptyConfig,
7358+
dispatcher,
7359+
replyResolver,
7360+
replyOptions: { hasRepliedRef },
7361+
});
7362+
7363+
expect(dispatcher.sendFinalReply).toHaveBeenCalledWith(reply);
7364+
});
7365+
7366+
it("keeps direct-chat final status text after source reply delivery", async () => {
7367+
setNoAbort();
7368+
const dispatcher = createDispatcher();
7369+
const hasRepliedRef = { value: true };
7370+
const ctx = buildTestCtx({ Provider: "slack", Surface: "slack", ChatType: "direct" });
7371+
const reply = { text: "Sent." } satisfies ReplyPayload;
7372+
const replyResolver = async () => reply;
7373+
7374+
await dispatchReplyFromConfig({
7375+
ctx,
7376+
cfg: emptyConfig,
7377+
dispatcher,
7378+
replyResolver,
7379+
replyOptions: { hasRepliedRef },
7380+
});
7381+
7382+
expect(dispatcher.sendFinalReply).toHaveBeenCalledWith(reply);
7383+
});
7384+
73107385
it("suppresses isReasoning payloads from block replies (generic dispatch path)", async () => {
73117386
setNoAbort();
73127387
const dispatcher = createDispatcher();

src/auto-reply/reply/dispatch-from-config.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,30 @@ async function maybeApplyTtsToReplyPayload(
362362
: copyReplyPayloadMetadata(params.payload, ttsPayload);
363363
}
364364

365+
function isLikelyInternalSourceReplyFinal(params: {
366+
reply: ReplyPayload;
367+
chatType: string | undefined;
368+
hasRepliedRef?: { value: boolean };
369+
}): boolean {
370+
if (params.chatType !== "group" && params.chatType !== "channel") {
371+
return false;
372+
}
373+
if (params.hasRepliedRef?.value !== true) {
374+
return false;
375+
}
376+
const parts = resolveSendableOutboundReplyParts(params.reply);
377+
if (!parts.hasText || parts.hasMedia) {
378+
return false;
379+
}
380+
const text = parts.trimmedText.replace(/\s+/g, " ");
381+
if (text.length > 500) {
382+
return false;
383+
}
384+
return /^(?:sent\.?|sent[,:; -]|replied(?:\s+in|\s+to)?\b|message sent\b|delivered\b|posted\b|done\b.*\b(?:posted|sent|replied|responded|delivered|confirmed|flagged)\b|confirmed\b.*\b(?:replied|responded|sent|flagged)\b|i (?:replied|responded|sent|flagged)\b)/i.test(
385+
text,
386+
);
387+
}
388+
365389
const resolveRoutedPolicyConversationType = (
366390
ctx: FinalizedMsgContext,
367391
): "direct" | "group" | undefined => {
@@ -3279,6 +3303,28 @@ export async function dispatchReplyFromConfig(
32793303
if (reply.isReasoning === true) {
32803304
continue;
32813305
}
3306+
if (
3307+
isLikelyInternalSourceReplyFinal({
3308+
reply,
3309+
chatType,
3310+
hasRepliedRef: params.replyOptions?.hasRepliedRef,
3311+
}) &&
3312+
!shouldDeliverDespiteSourceReplySuppression(reply)
3313+
) {
3314+
logVerbose(
3315+
[
3316+
"dispatch-from-config: final reply suppressed because a source reply was already delivered",
3317+
`(session=${acpDispatchSessionKey ?? sessionKey ?? "unknown"}`,
3318+
`provider=${ctx.Provider ?? "unknown"}`,
3319+
`surface=${ctx.Surface ?? "unknown"}`,
3320+
`chatType=${chatType ?? "unknown"}`,
3321+
`inboundEventKind=${ctx.InboundEventKind ?? "unknown"}`,
3322+
`message=${ctx.MessageSidFull ?? ctx.MessageSid ?? "unknown"}`,
3323+
`${formatSuppressedReplyPayloadForLog(reply)})`,
3324+
].join(" "),
3325+
);
3326+
continue;
3327+
}
32823328
if (suppressDelivery && !shouldDeliverDespiteSourceReplySuppression(reply)) {
32833329
if (hasOutboundReplyContent(reply, { trimText: true })) {
32843330
logVerbose(

0 commit comments

Comments
 (0)