Matrix: fix delayed draft block boundaries

This commit is contained in:
Gustavo Madeira Santana
2026-04-02 03:47:57 -04:00
parent 8748b7c54c
commit 68bb76519a
2 changed files with 48 additions and 7 deletions

View File

@@ -1729,6 +1729,50 @@ describe("matrix monitor handler draft streaming", () => {
await finish();
});
it("keeps delayed same-message block boundaries at the emitted block length", async () => {
const { dispatch, redactEventMock } = createStreamingHarness({ blockStreamingEnabled: true });
const { deliver, opts, finish } = await dispatch();
opts.onPartialReply?.({ text: "Alpha" });
await vi.waitFor(() => {
expect(sendSingleTextMessageMatrixMock).toHaveBeenCalledTimes(1);
});
opts.onPartialReply?.({ text: "AlphaBeta" });
await vi.waitFor(() => {
expect(editMessageMatrixMock).toHaveBeenCalledWith(
"!room:example.org",
"$draft1",
"AlphaBeta",
expect.anything(),
);
});
await opts.onBlockReplyQueued?.({ text: "Alpha" });
sendSingleTextMessageMatrixMock.mockClear();
editMessageMatrixMock.mockClear();
sendSingleTextMessageMatrixMock.mockResolvedValueOnce({
messageId: "$draft2",
roomId: "!room",
});
await deliver({ text: "Alpha" }, { kind: "block" });
await vi.waitFor(() => {
expect(sendSingleTextMessageMatrixMock).toHaveBeenCalledTimes(1);
});
expect(sendSingleTextMessageMatrixMock.mock.calls[0]?.[1]).toBe("Beta");
expect(editMessageMatrixMock).toHaveBeenCalledWith(
"!room:example.org",
"$draft1",
"Alpha",
expect.anything(),
);
expect(deliverMatrixRepliesMock).not.toHaveBeenCalled();
expect(redactEventMock).not.toHaveBeenCalled();
await finish();
});
it("falls back to deliverMatrixReplies when final edit fails", async () => {
const { dispatch } = createStreamingHarness();
const { deliver, opts, finish } = await dispatch();

View File

@@ -1184,13 +1184,10 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
const messageGeneration = context?.assistantMessageIndex ?? currentDraftMessageGeneration;
const lastQueuedDraftBoundaryOffset =
latestQueuedDraftBoundaryOffsets.get(messageGeneration) ?? 0;
const nextDraftBoundaryOffset =
messageGeneration === currentDraftMessageGeneration
? Math.max(
latestDraftFullText.length,
lastQueuedDraftBoundaryOffset + payloadTextLength,
)
: lastQueuedDraftBoundaryOffset + payloadTextLength;
// Logical block boundaries must follow emitted block text, not whichever
// later partial preview has already arrived by the time the async
// boundary callback drains.
const nextDraftBoundaryOffset = lastQueuedDraftBoundaryOffset + payloadTextLength;
latestQueuedDraftBoundaryOffsets.set(messageGeneration, nextDraftBoundaryOffset);
pendingDraftBoundaries.push({
messageGeneration,