






















@@ -1723,6 +1723,88 @@ describe("dispatchTelegramMessage draft streaming", () => {
17231723expect(groupHistories.get(historyKey)).toHaveLength(0);
17241724});
172517251726+it("does not clear topic room-event history for a send to another topic", async () => {
1727+const historyKey = "telegram:group:-100123:topic:77";
1728+const groupHistories = new Map([
1729+[historyKey, [{ sender: "Alice", body: "topic 77 context", timestamp: 1 }]],
1730+]);
1731+let firstStarted: (() => void) | undefined;
1732+const firstStartGate = new Promise<void>((resolve) => {
1733+firstStarted = resolve;
1734+});
1735+let releaseFirst: (() => void) | undefined;
1736+const firstGate = new Promise<void>((resolve) => {
1737+releaseFirst = resolve;
1738+});
1739+let secondStarted: (() => void) | undefined;
1740+const secondStartGate = new Promise<void>((resolve) => {
1741+secondStarted = resolve;
1742+});
1743+dispatchReplyWithBufferedBlockDispatcher
1744+.mockImplementationOnce(async () => {
1745+firstStarted?.();
1746+await firstGate;
1747+return {
1748+queuedFinal: false,
1749+counts: { block: 0, final: 0, tool: 0 },
1750+sourceReplyDeliveryMode: "message_tool_only",
1751+};
1752+})
1753+.mockImplementationOnce(async () => {
1754+secondStarted?.();
1755+return {
1756+queuedFinal: false,
1757+counts: { block: 0, final: 0, tool: 0 },
1758+sourceReplyDeliveryMode: "message_tool_only",
1759+};
1760+});
1761+1762+const createRoomContext = (messageId: number, body: string) =>
1763+createContext({
1764+ctxPayload: {
1765+InboundTurnKind: "room_event",
1766+SessionKey: "agent:main:telegram:group:-100123",
1767+ChatType: "group",
1768+MessageSid: String(messageId),
1769+RawBody: body,
1770+BodyForAgent: body,
1771+CommandBody: body,
1772+} as unknown as TelegramMessageContext["ctxPayload"],
1773+msg: {
1774+chat: { id: -100123, type: "supergroup", is_forum: true },
1775+message_id: messageId,
1776+message_thread_id: 77,
1777+} as unknown as TelegramMessageContext["msg"],
1778+chatId: -100123,
1779+isGroup: true,
1780+ historyKey,
1781+historyLimit: 10,
1782+ groupHistories,
1783+threadSpec: { id: 77, scope: "forum" },
1784+});
1785+1786+const firstPromise = dispatchWithContext({
1787+context: createRoomContext(99, "ambient one"),
1788+streamMode: "partial",
1789+});
1790+await firstStartGate;
1791+notifyTelegramInboundTurnOutboundSuccess({
1792+sessionKey: "agent:main:telegram:group:-100123",
1793+to: "telegram:group:-100123:topic:88",
1794+inboundTurnKind: "room_event",
1795+});
1796+const secondPromise = dispatchWithContext({
1797+context: createRoomContext(100, "ambient two"),
1798+streamMode: "partial",
1799+});
1800+1801+await secondStartGate;
1802+releaseFirst?.();
1803+await Promise.all([firstPromise, secondPromise]);
1804+1805+expect(groupHistories.get(historyKey)).toHaveLength(1);
1806+});
1807+17261808it("does not let room events supersede active user-request dispatch", async () => {
17271809const historyKey = "telegram:group:-100123";
17281810const groupHistories = new Map([[historyKey, []]]);
@@ -1894,6 +1976,73 @@ describe("dispatchTelegramMessage draft streaming", () => {
18941976expect(deliveredTexts).not.toContain("stale ambient answer");
18951977});
189619781979+it("keeps queued room events abortable after their source dispatch returns", async () => {
1980+const historyKey = "telegram:group:-100123";
1981+const groupHistories = new Map([[historyKey, []]]);
1982+let roomEventAbortSignal: AbortSignal | undefined;
1983+let queuedLifecycle: { onEnqueued?: () => void; onComplete?: () => void } | undefined;
1984+dispatchReplyWithBufferedBlockDispatcher
1985+.mockImplementationOnce(async ({ replyOptions }) => {
1986+roomEventAbortSignal = replyOptions?.abortSignal;
1987+queuedLifecycle = replyOptions?.queuedFollowupLifecycle;
1988+queuedLifecycle?.onEnqueued?.();
1989+return {
1990+queuedFinal: false,
1991+counts: { block: 0, final: 0, tool: 0 },
1992+sourceReplyDeliveryMode: "message_tool_only",
1993+};
1994+})
1995+.mockImplementationOnce(async ({ dispatcherOptions }) => {
1996+await dispatcherOptions.deliver({ text: "fresh request answer" }, { kind: "final" });
1997+return {
1998+queuedFinal: true,
1999+counts: { block: 0, final: 1, tool: 0 },
2000+};
2001+});
2002+2003+const createGroupContext = (
2004+kind: "user_request" | "room_event",
2005+messageId: number,
2006+body: string,
2007+) =>
2008+createContext({
2009+ctxPayload: {
2010+InboundTurnKind: kind,
2011+SessionKey: "agent:main:telegram:group:-100123",
2012+ChatType: "group",
2013+MessageSid: String(messageId),
2014+RawBody: body,
2015+BodyForAgent: body,
2016+CommandBody: body,
2017+CommandAuthorized: true,
2018+} as unknown as TelegramMessageContext["ctxPayload"],
2019+msg: {
2020+chat: { id: -100123, type: "supergroup" },
2021+message_id: messageId,
2022+} as unknown as TelegramMessageContext["msg"],
2023+chatId: -100123,
2024+isGroup: true,
2025+ historyKey,
2026+historyLimit: 10,
2027+ groupHistories,
2028+threadSpec: { id: undefined, scope: "none" },
2029+});
2030+2031+await dispatchWithContext({
2032+context: createGroupContext("room_event", 99, "ambient chatter"),
2033+streamMode: "off",
2034+});
2035+expect(roomEventAbortSignal?.aborted).toBe(false);
2036+2037+await dispatchWithContext({
2038+context: createGroupContext("user_request", 100, "@bot answer now"),
2039+streamMode: "off",
2040+});
2041+2042+expect(roomEventAbortSignal?.aborted).toBe(true);
2043+queuedLifecycle?.onComplete?.();
2044+});
2045+18972046it("does not send visible error fallbacks for room events", async () => {
18982047const historyKey = "telegram:group:-100123";
18992048const groupHistories = new Map([
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。