Skip to content

Commit aaa51a3

Browse files
committed
fix(sdk): fold queued response data parts into the managed error partial
The managed error path now folds data parts queued via chat.response / writer.write() into the recovered partial before persisting, matching the success path and the manual loop, so a data part queued just before a source-stream failure isn't dropped.
1 parent b5c37ee commit aaa51a3

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

packages/trigger-sdk/src/v3/ai.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7956,6 +7956,16 @@ function chatAgent<
79567956
if (partialResponse && !partialResponse.id) {
79577957
partialResponse = { ...partialResponse, id: generateMessageId() } as TUIMessage;
79587958
}
7959+
if (partialResponse && !responseCommitted) {
7960+
const queuedParts = locals.get(chatResponsePartsKey);
7961+
if (queuedParts && queuedParts.length > 0) {
7962+
partialResponse = {
7963+
...partialResponse,
7964+
parts: [...partialResponse.parts, ...(queuedParts as UIMessage["parts"])],
7965+
} as TUIMessage;
7966+
locals.set(chatResponsePartsKey, []);
7967+
}
7968+
}
79597969
const includePartial = partialResponse != null && !responseCommitted;
79607970
let erroredUIMessagesWithPartial: TUIMessage[] = !includePartial
79617971
? erroredUIMessages
@@ -7991,7 +8001,9 @@ function chatAgent<
79918001
}
79928002
if (erroredUIMessagesWithPartial !== accumulatedUIMessages) {
79938003
if (partialIdx === -1) {
7994-
const appended = erroredUIMessagesWithPartial.slice(accumulatedUIMessages.length);
8004+
const appended = erroredUIMessagesWithPartial.slice(
8005+
accumulatedUIMessages.length
8006+
);
79958007
accumulatedMessages.push(
79968008
...(await toModelMessages(appended.map((m) => stripProviderMetadata(m))))
79978009
);

packages/trigger-sdk/test/chat-agent-source-stream-error.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,35 @@ describe("chat.agent managed loop — source-stream failure", () => {
368368
await harness.close();
369369
}
370370
});
371+
372+
it("folds queued response data parts into the recovered partial", async () => {
373+
const turnCompletes: TurnCompleteEvent<unknown, UIMessage>[] = [];
374+
375+
const agent = chat.agent({
376+
id: "chatAgent.error-queued-parts",
377+
run: async () => {
378+
chat.response.write({ type: "data-marker", data: { kept: true } } as never);
379+
return erroringSource("UND_ERR_BODY_TIMEOUT") as never;
380+
},
381+
onTurnComplete: async (event) => {
382+
turnCompletes.push(event);
383+
},
384+
});
385+
386+
const harness = mockChatAgent(agent, { chatId: "cae-error-queued-parts" });
387+
try {
388+
await harness.sendMessage(userMessage("hi", "u-1"));
389+
await waitFor(() => turnCompletes.length >= 1);
390+
391+
const evt = turnCompletes[0]!;
392+
expect(evt.responseMessage).toBeDefined();
393+
const parts = evt.responseMessage!.parts as Array<{ type: string }>;
394+
expect(extractText(evt.responseMessage)).toBe("partial answer");
395+
expect(parts.some((p) => p.type === "data-marker")).toBe(true);
396+
} finally {
397+
await harness.close();
398+
}
399+
});
371400
});
372401

373402
describe("chat.createSession turn.complete() — source-stream failure", () => {

0 commit comments

Comments
 (0)