



























@@ -3468,6 +3468,84 @@ describe("dispatchTelegramMessage draft streaming", () => {
34683468expect(firstAnswerDraft.clear).not.toHaveBeenCalled();
34693469});
347034703471+it("ignores stale answer finalization after a newer message supersedes the same session", async () => {
3472+let releaseFirstFinal!: () => void;
3473+const firstFinalGate = new Promise<void>((resolve) => {
3474+releaseFirstFinal = resolve;
3475+});
3476+let resolvePreviewVisible!: () => void;
3477+const previewVisible = new Promise<void>((resolve) => {
3478+resolvePreviewVisible = resolve;
3479+});
3480+3481+const firstAnswerDraft = createTestDraftStream({
3482+messageId: 1001,
3483+onUpdate: (text) => {
3484+if (text === "Old reply partial") {
3485+resolvePreviewVisible();
3486+}
3487+},
3488+});
3489+const firstReasoningDraft = createDraftStream();
3490+const secondAnswerDraft = createDraftStream();
3491+const secondReasoningDraft = createDraftStream();
3492+createTelegramDraftStream
3493+.mockImplementationOnce(() => firstAnswerDraft)
3494+.mockImplementationOnce(() => firstReasoningDraft)
3495+.mockImplementationOnce(() => secondAnswerDraft)
3496+.mockImplementationOnce(() => secondReasoningDraft);
3497+dispatchReplyWithBufferedBlockDispatcher
3498+.mockImplementationOnce(async ({ dispatcherOptions, replyOptions }) => {
3499+await replyOptions?.onPartialReply?.({ text: "Old reply partial" });
3500+await firstFinalGate;
3501+await dispatcherOptions.deliver({ text: "Old reply final" }, { kind: "final" });
3502+return { queuedFinal: true };
3503+})
3504+.mockImplementationOnce(async ({ dispatcherOptions }) => {
3505+await dispatcherOptions.deliver({ text: "New reply final" }, { kind: "final" });
3506+return { queuedFinal: true };
3507+});
3508+const newReplyDelivered = observeDeliveredReply("New reply final");
3509+editMessageTelegram.mockResolvedValue({ ok: true, chatId: "123", messageId: "1001" });
3510+3511+const firstPromise = dispatchWithContext({
3512+context: createContext({
3513+ctxPayload: {
3514+SessionKey: "s1",
3515+Body: "earlier request",
3516+RawBody: "earlier request",
3517+MessageSid: "msg-1",
3518+} as never,
3519+}),
3520+});
3521+3522+await previewVisible;
3523+3524+const secondPromise = dispatchWithContext({
3525+context: createContext({
3526+ctxPayload: {
3527+SessionKey: "s1",
3528+Body: "newer request",
3529+RawBody: "newer request",
3530+MessageSid: "msg-2",
3531+} as never,
3532+}),
3533+});
3534+3535+await newReplyDelivered;
3536+3537+releaseFirstFinal();
3538+await Promise.all([firstPromise, secondPromise]);
3539+3540+expect(editMessageTelegram).not.toHaveBeenCalledWith(
3541+123,
3542+1001,
3543+"Old reply final",
3544+expect.any(Object),
3545+);
3546+expect(firstAnswerDraft.clear).not.toHaveBeenCalled();
3547+});
3548+34713549it("discards hidden short partials instead of flushing a stale preview after abort", async () => {
34723550let releaseFirstCleanup!: () => void;
34733551const firstCleanupGate = new Promise<void>((resolve) => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。