fix: keep telegram room events fully quiet

This commit is contained in:
Peter Steinberger
2026-05-15 15:50:28 +01:00
parent b5fde36c41
commit 4b11d65ada
4 changed files with 99 additions and 1 deletions

View File

@@ -46,6 +46,42 @@ describe("telegram inbound turn delivery", () => {
end();
});
it("matches provider-prefixed Telegram targets for delivery correlation", () => {
let count = 0;
const end = beginTelegramInboundTurnDeliveryCorrelation("sess:prefixed", {
outboundTo: "-100123",
markInboundTurnDelivered: () => {
count += 1;
},
});
notifyTelegramInboundTurnOutboundSuccess({
sessionKey: "sess:prefixed",
to: "telegram:-100123",
});
expect(count).toBe(1);
end();
});
it("matches Telegram topic targets by conversation for delivery correlation", () => {
let count = 0;
const end = beginTelegramInboundTurnDeliveryCorrelation("sess:topic", {
outboundTo: "-100123",
markInboundTurnDelivered: () => {
count += 1;
},
});
notifyTelegramInboundTurnOutboundSuccess({
sessionKey: "sess:topic",
to: "telegram:-100123:topic:77",
});
expect(count).toBe(1);
end();
});
it("keeps user-request and room-event delivery correlations separate", () => {
let userRequestCount = 0;
let roomEventCount = 0;

View File

@@ -9,6 +9,30 @@ type ActiveTurn = {
const registry = new Map<string, ActiveTurn>();
function normalizeTelegramDeliveryTarget(value: string): string {
return value
.trim()
.toLowerCase()
.replace(/^(telegram|tg):/u, "");
}
function stripTelegramTopicTarget(value: string): string {
return value.replace(/:topic:\d+$/u, "");
}
function telegramDeliveryTargetsMatch(expected: string, actual: string): boolean {
const expectedTarget = normalizeTelegramDeliveryTarget(expected);
const actualTarget = normalizeTelegramDeliveryTarget(actual);
if (expectedTarget === actualTarget) {
return true;
}
const expectedBase = stripTelegramTopicTarget(expectedTarget);
const actualBase = stripTelegramTopicTarget(actualTarget);
return (
expectedBase === actualBase && (expectedTarget === expectedBase || actualTarget === actualBase)
);
}
export function resolveTelegramInboundTurnDeliveryCorrelationKey(
sessionKey: string | undefined,
inboundTurnKind?: TelegramInboundTurnDeliveryKind | string,
@@ -52,7 +76,7 @@ export function notifyTelegramInboundTurnOutboundSuccess(params: {
return;
}
const turn = registry.get(key);
if (!turn || turn.outboundTo !== params.to) {
if (!turn || !telegramDeliveryTargetsMatch(turn.outboundTo, params.to)) {
return;
}
if (turn.outboundAccountId && params.accountId && params.accountId !== turn.outboundAccountId) {

View File

@@ -4785,6 +4785,43 @@ describe("sendPolicy deny — suppress delivery, not processing (#53328)", () =>
expect(dispatcher.sendToolResult).not.toHaveBeenCalled();
});
it("suppresses marked runtime failure notices for room events", async () => {
setNoAbort();
sessionStoreMocks.currentEntry = {
sessionId: "s1",
updatedAt: 0,
sendPolicy: "allow",
};
const dispatcher = createDispatcher();
const failureNotice = setReplyPayloadMetadata(
{ text: "⚠️ You've reached your Codex subscription usage limit." },
{ deliverDespiteSourceReplySuppression: true },
);
const replyResolver = vi.fn(async () => failureNotice satisfies ReplyPayload);
const ctx = buildTestCtx({
ChatType: "group",
InboundTurnKind: "room_event",
SessionKey: "test:session",
});
const result = await dispatchReplyFromConfig({
ctx,
cfg: emptyConfig,
dispatcher,
replyResolver,
replyOptions: {
sourceReplyDeliveryMode: "message_tool_only",
},
});
expect(replyResolver).toHaveBeenCalledTimes(1);
expect(result.queuedFinal).toBe(false);
expect(result.sourceReplyDeliveryMode).toBe("message_tool_only");
expect(dispatcher.sendFinalReply).not.toHaveBeenCalled();
expect(dispatcher.sendBlockReply).not.toHaveBeenCalled();
expect(dispatcher.sendToolResult).not.toHaveBeenCalled();
});
it("mirrors internal source reply payloads into the active transcript", async () => {
setNoAbort();
sessionStoreMocks.currentEntry = {

View File

@@ -1599,6 +1599,7 @@ export async function dispatchReplyFromConfig(
let finalDeliveryFailed = false;
const shouldDeliverDespiteSourceReplySuppression = (reply: ReplyPayload) =>
suppressAutomaticSourceDelivery &&
ctx.InboundTurnKind !== "room_event" &&
!sendPolicyDenied &&
getReplyPayloadMetadata(reply)?.deliverDespiteSourceReplySuppression === true;
for (const reply of replies) {