Coalesce prepended tool call history

This commit is contained in:
Mohamed Boudra
2026-05-17 20:53:03 +07:00
parent e27734b218
commit c4e45af565
3 changed files with 163 additions and 34 deletions
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import type { AgentStreamEventPayload } from "@server/shared/messages";
import type { StreamItem } from "@/types/stream";
import { hydrateStreamState, type AgentToolCallItem, type StreamItem } from "@/types/stream";
import {
createAgentStreamReducerQueue,
processTimelineResponse,
@@ -31,6 +31,28 @@ function makeTimelineEntry(
};
}
function makeToolCallTimelineEntry(
seq: number,
callId: string,
status: "running" | "completed",
detail: Record<string, unknown>,
) {
return {
seqStart: seq,
seqEnd: seq,
provider: "claude",
item: {
type: "tool_call",
callId,
name: "Read",
status,
detail,
error: null,
},
timestamp: new Date(1000 + seq).toISOString(),
};
}
function makeTimelineEvent(
text: string,
type: string = "assistant_message",
@@ -97,6 +119,13 @@ function getUserTexts(items: StreamItem[]): string[] {
.map((item) => item.text);
}
function getAgentToolCalls(items: StreamItem[]) {
return items.filter(
(item): item is AgentToolCallItem =>
item.kind === "tool_call" && item.payload.source === "agent",
);
}
const baseTimelineInput: ProcessTimelineResponseInput = {
payload: {
agentId: "agent-1",
@@ -510,6 +539,68 @@ describe("processTimelineResponse", () => {
});
});
it("coalesces tool call lifecycle rows across the older-page prepend boundary", () => {
const callId = "toolu_boundary";
const currentTail = hydrateStreamState(
[
{
event: {
type: "timeline",
provider: "claude",
item: makeToolCallTimelineEntry(3, callId, "completed", {
type: "read",
filePath: "/tmp/example.ts",
}).item,
} as AgentStreamEventPayload,
timestamp: new Date(3000),
},
],
{ source: "canonical" },
);
const existingCursor: TimelineCursor = {
epoch: "epoch-1",
startSeq: 3,
endSeq: 5,
};
const result = processTimelineResponse({
...baseTimelineInput,
currentTail,
currentCursor: existingCursor,
payload: {
...baseTimelineInput.payload,
direction: "before",
epoch: "epoch-1",
startCursor: { seq: 1 },
endCursor: { seq: 2 },
entries: [
makeToolCallTimelineEntry(1, callId, "running", {
type: "unknown",
input: { file_path: "/tmp/example.ts" },
output: null,
}),
],
},
});
const toolCalls = getAgentToolCalls(result.tail);
expect(
toolCalls.map((item) => ({
id: item.id,
callId: item.payload.data.callId,
status: item.payload.data.status,
detailType: item.payload.data.detail.type,
})),
).toEqual([
{
id: `agent_tool_${callId}`,
callId,
status: "completed",
detailType: "read",
},
]);
});
it("requests canonical catch-up when a projected entry overlaps unseen seqs", () => {
const existingCursor: TimelineCursor = {
epoch: "epoch-1",