diff --git a/hub/src/feishu/card/streaming-card.ts b/hub/src/feishu/card/streaming-card.ts index f703e0c..27e4f86 100644 --- a/hub/src/feishu/card/streaming-card.ts +++ b/hub/src/feishu/card/streaming-card.ts @@ -173,17 +173,20 @@ export class StreamingAgentCard { ); } - let updated = true; + let cardUpdated = true; if (answerText.length > 0 || segments.length > 0) { - updated = await this.flushCard("complete", answerText, isError, segments); + cardUpdated = await this.flushCard("complete", answerText, isError, segments); } else if (this.currentMessageId !== null) { - updated = await this.flushCard("complete", "", isError, []); + cardUpdated = await this.flushCard("complete", "", isError, []); } - if (!updated) { + if (!cardUpdated) { // Card path failed (e.g. residual content policy). Deliver text + standalone images. - updated = await this.deliverPlainFallback(segments, answerText); + await this.deliverPlainFallback(segments, answerText); } - if (!updated && this.interrupted) { + // Interrupt is terminal; if the live card could not be finalized, always + // send an explicit notice so the teacher sees the abort even when plain + // text partial delivery succeeded. + if (!cardUpdated && this.interrupted) { await sendText(this.rt, this.chatId, "\u5DF2\u4E2D\u65AD\u5F53\u524D\u8FD0\u884C\u3002", this.sendOptions); } } finally { diff --git a/hub/test/integration/trigger.test.ts b/hub/test/integration/trigger.test.ts index ab855c2..73ef63d 100644 --- a/hub/test/integration/trigger.test.ts +++ b/hub/test/integration/trigger.test.ts @@ -1611,7 +1611,9 @@ describe("trigger full lifecycle (integration)", () => { expect(runs[0]?.status).toBe("CANCELED"); }); expect(patch).toHaveBeenCalled(); - expect(rt.sentTexts).toContain("已中断当前运行。"); + await vi.waitFor(() => { + expect(rt.sentTexts).toContain("已中断当前运行。"); + }); }); it("denies interrupt when the operator lacks agent.cancel permission", async () => {