import { describe, expect, it } from "vitest";import { computeStableMessagesTimelineRows, computeMessageDurationStart, deriveMessagesTimelineRows, normalizeCompactToolLabel, resolveAssistantMessageCopyState,} from "./MessagesTimeline.logic"; describe("computeMessageDurationStart", () => { it("returns message createdAt when there is no preceding user message", () => { const result = computeMessageDurationStart([ { id: "a1", role: "assistant", createdAt: "2026-01-01T00:00:05Z", completedAt: "2026-01-01T00:00:10Z", }, ]); expect(result).toEqual(new Map([["a1", "2026-01-01T00:00:05Z"]])); }); it("uses the user message createdAt for the first assistant response", () => { const result = computeMessageDurationStart([ { id: "u1", role: "user", createdAt: "2026-01-01T00:00:00Z" }, { id: "a1", role: "assistant", createdAt: "2026-01-01T00:00:30Z", completedAt: "2026-01-01T00:00:30Z", }, ]); expect(result).toEqual( new Map([ ["u1", "2026-01-01T00:00:00Z"], ["a1", "2026-01-01T00:00:00Z"], ]), ); }); it("uses the previous assistant completedAt for subsequent assistant responses", () => { const result = computeMessageDurationStart([ { id: "u1", role: "user", createdAt: "2026-01-01T00:00:00Z" }, { id: "a1", role: "assistant", createdAt: "2026-01-01T00:00:30Z", completedAt: "2026-01-01T00:00:30Z", }, { id: "a2", role: "assistant", createdAt: "2026-01-01T00:00:55Z", completedAt: "2026-01-01T00:00:55Z", }, ]); expect(result).toEqual( new Map([ ["u1", "2026-01-01T00:00:00Z"], ["a1", "2026-01-01T00:00:00Z"], ["a2", "2026-01-01T00:00:30Z"], ]), ); }); it("does not advance the boundary for a streaming message without completedAt", () => { const result = computeMessageDurationStart([ { id: "u1", role: "user", createdAt: "2026-01-01T00:00:00Z" }, { id: "a1", role: "assistant", createdAt: "2026-01-01T00:00:30Z" }, { id: "a2", role: "assistant", createdAt: "2026-01-01T00:00:55Z", completedAt: "2026-01-01T00:00:55Z", }, ]); expect(result).toEqual( new Map([ ["u1", "2026-01-01T00:00:00Z"], ["a1", "2026-01-01T00:00:00Z"], ["a2", "2026-01-01T00:00:00Z"], ]), ); }); it("resets the boundary on a new user message", () => { const result = computeMessageDurationStart([ { id: "u1", role: "user", createdAt: "2026-01-01T00:00:00Z" }, { id: "a1", role: "assistant", createdAt: "2026-01-01T00:00:30Z", completedAt: "2026-01-01T00:00:30Z", }, { id: "u2", role: "user", createdAt: "2026-01-01T00:01:00Z" }, { id: "a2", role: "assistant", createdAt: "2026-01-01T00:01:20Z", completedAt: "2026-01-01T00:01:20Z", }, ]); expect(result).toEqual( new Map([ ["u1", "2026-01-01T00:00:00Z"], ["a1", "2026-01-01T00:00:00Z"], ["u2", "2026-01-01T00:01:00Z"], ["a2", "2026-01-01T00:01:00Z"], ]), ); }); it("handles system messages without affecting the boundary", () => { const result = computeMessageDurationStart([ { id: "u1", role: "user", createdAt: "2026-01-01T00:00:00Z" }, { id: "s1", role: "system", createdAt: "2026-01-01T00:00:01Z" }, { id: "a1", role: "assistant", createdAt: "2026-01-01T00:00:30Z", completedAt: "2026-01-01T00:00:30Z", }, ]); expect(result).toEqual( new Map([ ["u1", "2026-01-01T00:00:00Z"], ["s1", "2026-01-01T00:00:00Z"], ["a1", "2026-01-01T00:00:00Z"], ]), ); }); it("returns empty map for empty input", () => { expect(computeMessageDurationStart([])).toEqual(new Map()); });}); describe("normalizeCompactToolLabel", () => { it("removes trailing completion wording from command labels", () => { expect(normalizeCompactToolLabel("Ran command complete")).toBe("Ran command"); }); it("removes trailing completion wording from other labels", () => { expect(normalizeCompactToolLabel("Read file completed")).toBe("Read file"); });}); describe("resolveAssistantMessageCopyState", () => { it("returns enabled copy state for completed assistant messages", () => { expect( resolveAssistantMessageCopyState({ showCopyButton: true, text: "Ship it", streaming: false, }), ).toEqual({ text: "Ship it", visible: true, }); }); it("hides copy while an assistant message is still streaming", () => { expect( resolveAssistantMessageCopyState({ showCopyButton: true, text: "Still streaming", streaming: true, }), ).toEqual({ text: "Still streaming", visible: false, }); }); it("hides copy for empty completed assistant messages", () => { expect( resolveAssistantMessageCopyState({ showCopyButton: true, text: " ", streaming: false, }), ).toEqual({ text: null, visible: false, }); }); it("hides copy for non-terminal assistant messages", () => { expect( resolveAssistantMessageCopyState({ showCopyButton: false, text: "Interim thought", streaming: false, }), ).toEqual({ text: "Interim thought", visible: false, }); });}); describe("deriveMessagesTimelineRows", () => { it("only enables assistant copy for the terminal assistant message in a turn", () => { const rows = deriveMessagesTimelineRows({ timelineEntries: [ { id: "user-1-entry", kind: "message", createdAt: "2026-01-01T00:00:00Z", message: { id: "user-1" as never, role: "user", text: "Write a poem", turnId: null, createdAt: "2026-01-01T00:00:00Z", streaming: false, }, }, { id: "assistant-thought-entry", kind: "message", createdAt: "2026-01-01T00:00:10Z", message: { id: "assistant-thought" as never, role: "assistant", text: "I should ground this first.", turnId: "turn-1" as never, createdAt: "2026-01-01T00:00:10Z", completedAt: "2026-01-01T00:00:11Z", streaming: false, }, }, { id: "assistant-final-entry", kind: "message", createdAt: "2026-01-01T00:00:20Z", message: { id: "assistant-final" as never, role: "assistant", text: "Here is the poem.", turnId: "turn-1" as never, createdAt: "2026-01-01T00:00:20Z", completedAt: "2026-01-01T00:00:30Z", streaming: false, }, }, ], completionDividerBeforeEntryId: "assistant-final-entry", isWorking: false, activeTurnStartedAt: null, turnDiffSummaryByAssistantMessageId: new Map(), revertTurnCountByUserMessageId: new Map(), }); const assistantRows = rows.filter( (row): row is Extract<(typeof rows)[number], { kind: "message" }> => row.kind === "message" && row.message.role === "assistant", ); expect(assistantRows).toHaveLength(2); expect(assistantRows[0]?.showAssistantCopyButton).toBe(false); expect(assistantRows[1]?.showAssistantCopyButton).toBe(true); expect(assistantRows[1]?.showCompletionDivider).toBe(true); }); it("projects assistant diff summaries and user revert counts onto the affected rows", () => { const assistantTurnDiffSummary = { turnId: "turn-1" as never, completedAt: "2026-01-01T00:00:30Z", assistantMessageId: "assistant-1" as never, checkpointTurnCount: 2, files: [{ path: "src/index.ts", additions: 3, deletions: 1 }], }; const rows = deriveMessagesTimelineRows({ timelineEntries: [ { id: "user-entry", kind: "message", createdAt: "2026-01-01T00:00:00Z", message: { id: "user-1" as never, role: "user", text: "Do the thing", turnId: null, createdAt: "2026-01-01T00:00:00Z", streaming: false, }, }, { id: "assistant-entry", kind: "message", createdAt: "2026-01-01T00:00:20Z", message: { id: "assistant-1" as never, role: "assistant", text: "Done", turnId: "turn-1" as never, createdAt: "2026-01-01T00:00:20Z", completedAt: "2026-01-01T00:00:30Z", streaming: false, }, }, ], completionDividerBeforeEntryId: null, isWorking: false, activeTurnStartedAt: null, turnDiffSummaryByAssistantMessageId: new Map([ ["assistant-1" as never, assistantTurnDiffSummary], ]), revertTurnCountByUserMessageId: new Map([["user-1" as never, 1]]), }); const userRow = rows.find( (row): row is Extract<(typeof rows)[number], { kind: "message" }> => row.kind === "message" && row.message.role === "user", ); const assistantRow = rows.find( (row): row is Extract<(typeof rows)[number], { kind: "message" }> => row.kind === "message" && row.message.role === "assistant", ); expect(userRow?.revertTurnCount).toBe(1); expect(assistantRow?.assistantTurnDiffSummary).toBe(assistantTurnDiffSummary); });}); describe("computeStableMessagesTimelineRows", () => { it("returns the previous result when row order and content are unchanged", () => { const firstUserMessage = { id: "user-1" as never, role: "user" as const, text: "First", turnId: null, createdAt: "2026-01-01T00:00:00Z", streaming: false, }; const secondUserMessage = { id: "user-2" as never, role: "user" as const, text: "Second", turnId: null, createdAt: "2026-01-01T00:00:10Z", streaming: false, }; const rows = deriveMessagesTimelineRows({ timelineEntries: [ { id: "entry-user-1", kind: "message", createdAt: firstUserMessage.createdAt, message: firstUserMessage, }, { id: "entry-user-2", kind: "message", createdAt: secondUserMessage.createdAt, message: secondUserMessage, }, ], completionDividerBeforeEntryId: null, isWorking: false, activeTurnStartedAt: null, turnDiffSummaryByAssistantMessageId: new Map(), revertTurnCountByUserMessageId: new Map(), }); const initial = computeStableMessagesTimelineRows(rows, { byId: new Map(), result: [], }); const repeated = computeStableMessagesTimelineRows(rows, initial); expect(repeated).toBe(initial); expect(repeated.result).toBe(initial.result); }); it("reuses work rows when equivalent timeline derivations create new grouped arrays", () => { const firstWorkEntry = { id: "work-1", createdAt: "2026-01-01T00:00:00Z", label: "thinking", detail: "Inspecting repository state", tone: "thinking" as const, }; const secondWorkEntry = { id: "work-2", createdAt: "2026-01-01T00:00:01Z", label: "read", detail: "Reading package.json", tone: "tool" as const, }; const createRows = () => deriveMessagesTimelineRows({ timelineEntries: [ { id: "entry-work-1", kind: "work", createdAt: firstWorkEntry.createdAt, entry: firstWorkEntry, }, { id: "entry-work-2", kind: "work", createdAt: secondWorkEntry.createdAt, entry: secondWorkEntry, }, ], completionDividerBeforeEntryId: null, isWorking: false, activeTurnStartedAt: null, turnDiffSummaryByAssistantMessageId: new Map(), revertTurnCountByUserMessageId: new Map(), }); const firstRows = createRows(); const initial = computeStableMessagesTimelineRows(firstRows, { byId: new Map(), result: [], }); const secondRows = createRows(); expect(secondRows[0]).not.toBe(firstRows[0]); const repeated = computeStableMessagesTimelineRows(secondRows, initial); expect(repeated).toBe(initial); expect(repeated.result[0]).toBe(initial.result[0]); }); it("returns a new result when row order changes without content changes", () => { const firstUserMessage = { id: "user-1" as never, role: "user" as const, text: "First", turnId: null, createdAt: "2026-01-01T00:00:00Z", streaming: false, }; const secondUserMessage = { id: "user-2" as never, role: "user" as const, text: "Second", turnId: null, createdAt: "2026-01-01T00:00:10Z", streaming: false, }; const firstRows = deriveMessagesTimelineRows({ timelineEntries: [ { id: "entry-user-1", kind: "message", createdAt: firstUserMessage.createdAt, message: firstUserMessage, }, { id: "entry-user-2", kind: "message", createdAt: secondUserMessage.createdAt, message: secondUserMessage, }, ], completionDividerBeforeEntryId: null, isWorking: false, activeTurnStartedAt: null, turnDiffSummaryByAssistantMessageId: new Map(), revertTurnCountByUserMessageId: new Map(), }); const initial = computeStableMessagesTimelineRows(firstRows, { byId: new Map(), result: [], }); const reordered = computeStableMessagesTimelineRows([firstRows[1]!, firstRows[0]!], initial); expect(reordered).not.toBe(initial); expect(reordered.result).toEqual([initial.result[1], initial.result[0]]); });});
import { describe, expect, it } from "vitest";
import {
computeStableMessagesTimelineRows,
computeMessageDurationStart,
deriveMessagesTimelineRows,
normalizeCompactToolLabel,
resolveAssistantMessageCopyState,
} from "./MessagesTimeline.logic";
describe("computeMessageDurationStart", () => {
it("returns message createdAt when there is no preceding user message", () => {
const result = computeMessageDurationStart([
{
id: "a1",
role: "assistant",
createdAt: "2026-01-01T00:00:05Z",
completedAt: "2026-01-01T00:00:10Z",
},
]);
expect(result).toEqual(new Map([["a1", "2026-01-01T00:00:05Z"]]));
});
it("uses the user message createdAt for the first assistant response", () => {
const result = computeMessageDurationStart([
{ id: "u1", role: "user", createdAt: "2026-01-01T00:00:00Z" },
{
id: "a1",
role: "assistant",
createdAt: "2026-01-01T00:00:30Z",
completedAt: "2026-01-01T00:00:30Z",
},
]);
expect(result).toEqual(
new Map([
["u1", "2026-01-01T00:00:00Z"],
["a1", "2026-01-01T00:00:00Z"],
]),
);
});
it("uses the previous assistant completedAt for subsequent assistant responses", () => {
const result = computeMessageDurationStart([
{ id: "u1", role: "user", createdAt: "2026-01-01T00:00:00Z" },
{
id: "a1",
role: "assistant",
createdAt: "2026-01-01T00:00:30Z",
completedAt: "2026-01-01T00:00:30Z",
},
{
id: "a2",
role: "assistant",
createdAt: "2026-01-01T00:00:55Z",
completedAt: "2026-01-01T00:00:55Z",
},
]);
expect(result).toEqual(
new Map([
["u1", "2026-01-01T00:00:00Z"],
["a1", "2026-01-01T00:00:00Z"],
["a2", "2026-01-01T00:00:30Z"],
]),
);
});
it("does not advance the boundary for a streaming message without completedAt", () => {
const result = computeMessageDurationStart([
{ id: "u1", role: "user", createdAt: "2026-01-01T00:00:00Z" },
{ id: "a1", role: "assistant", createdAt: "2026-01-01T00:00:30Z" },
{
id: "a2",
role: "assistant",
createdAt: "2026-01-01T00:00:55Z",
completedAt: "2026-01-01T00:00:55Z",
},
]);
expect(result).toEqual(
new Map([
["u1", "2026-01-01T00:00:00Z"],
["a1", "2026-01-01T00:00:00Z"],
["a2", "2026-01-01T00:00:00Z"],
]),
);
});
it("resets the boundary on a new user message", () => {
const result = computeMessageDurationStart([
{ id: "u1", role: "user", createdAt: "2026-01-01T00:00:00Z" },
{
id: "a1",
role: "assistant",
createdAt: "2026-01-01T00:00:30Z",
completedAt: "2026-01-01T00:00:30Z",
},
{ id: "u2", role: "user", createdAt: "2026-01-01T00:01:00Z" },
{
id: "a2",
role: "assistant",
createdAt: "2026-01-01T00:01:20Z",
completedAt: "2026-01-01T00:01:20Z",
},
]);
expect(result).toEqual(
new Map([
["u1", "2026-01-01T00:00:00Z"],
["a1", "2026-01-01T00:00:00Z"],
["u2", "2026-01-01T00:01:00Z"],
["a2", "2026-01-01T00:01:00Z"],
]),
);
});
it("handles system messages without affecting the boundary", () => {
const result = computeMessageDurationStart([
{ id: "u1", role: "user", createdAt: "2026-01-01T00:00:00Z" },
{ id: "s1", role: "system", createdAt: "2026-01-01T00:00:01Z" },
{
id: "a1",
role: "assistant",
createdAt: "2026-01-01T00:00:30Z",
completedAt: "2026-01-01T00:00:30Z",
},
]);
expect(result).toEqual(
new Map([
["u1", "2026-01-01T00:00:00Z"],
["s1", "2026-01-01T00:00:00Z"],
["a1", "2026-01-01T00:00:00Z"],
]),
);
});
it("returns empty map for empty input", () => {
expect(computeMessageDurationStart([])).toEqual(new Map());
});
});
describe("normalizeCompactToolLabel", () => {
it("removes trailing completion wording from command labels", () => {
expect(normalizeCompactToolLabel("Ran command complete")).toBe("Ran command");
});
it("removes trailing completion wording from other labels", () => {
expect(normalizeCompactToolLabel("Read file completed")).toBe("Read file");
});
});
describe("resolveAssistantMessageCopyState", () => {
it("returns enabled copy state for completed assistant messages", () => {
expect(
resolveAssistantMessageCopyState({
showCopyButton: true,
text: "Ship it",
streaming: false,
}),
).toEqual({
text: "Ship it",
visible: true,
});
});
it("hides copy while an assistant message is still streaming", () => {
expect(
resolveAssistantMessageCopyState({
showCopyButton: true,
text: "Still streaming",
streaming: true,
}),
).toEqual({
text: "Still streaming",
visible: false,
});
});
it("hides copy for empty completed assistant messages", () => {
expect(
resolveAssistantMessageCopyState({
showCopyButton: true,
text: " ",
streaming: false,
}),
).toEqual({
text: null,
visible: false,
});
});
it("hides copy for non-terminal assistant messages", () => {
expect(
resolveAssistantMessageCopyState({
showCopyButton: false,
text: "Interim thought",
streaming: false,
}),
).toEqual({
text: "Interim thought",
visible: false,
});
});
});
describe("deriveMessagesTimelineRows", () => {
it("only enables assistant copy for the terminal assistant message in a turn", () => {
const rows = deriveMessagesTimelineRows({
timelineEntries: [
{
id: "user-1-entry",
kind: "message",
createdAt: "2026-01-01T00:00:00Z",
message: {
id: "user-1" as never,
role: "user",
text: "Write a poem",
turnId: null,
createdAt: "2026-01-01T00:00:00Z",
streaming: false,
},
},
{
id: "assistant-thought-entry",
kind: "message",
createdAt: "2026-01-01T00:00:10Z",
message: {
id: "assistant-thought" as never,
role: "assistant",
text: "I should ground this first.",
turnId: "turn-1" as never,
createdAt: "2026-01-01T00:00:10Z",
completedAt: "2026-01-01T00:00:11Z",
streaming: false,
},
},
{
id: "assistant-final-entry",
kind: "message",
createdAt: "2026-01-01T00:00:20Z",
message: {
id: "assistant-final" as never,
role: "assistant",
text: "Here is the poem.",
turnId: "turn-1" as never,
createdAt: "2026-01-01T00:00:20Z",
completedAt: "2026-01-01T00:00:30Z",
streaming: false,
},
},
],
completionDividerBeforeEntryId: "assistant-final-entry",
isWorking: false,
activeTurnStartedAt: null,
turnDiffSummaryByAssistantMessageId: new Map(),
revertTurnCountByUserMessageId: new Map(),
});
const assistantRows = rows.filter(
(row): row is Extract<(typeof rows)[number], { kind: "message" }> =>
row.kind === "message" && row.message.role === "assistant",
);
expect(assistantRows).toHaveLength(2);
expect(assistantRows[0]?.showAssistantCopyButton).toBe(false);
expect(assistantRows[1]?.showAssistantCopyButton).toBe(true);
expect(assistantRows[1]?.showCompletionDivider).toBe(true);
});
it("projects assistant diff summaries and user revert counts onto the affected rows", () => {
const assistantTurnDiffSummary = {
turnId: "turn-1" as never,
completedAt: "2026-01-01T00:00:30Z",
assistantMessageId: "assistant-1" as never,
checkpointTurnCount: 2,
files: [{ path: "src/index.ts", additions: 3, deletions: 1 }],
};
const rows = deriveMessagesTimelineRows({
timelineEntries: [
{
id: "user-entry",
kind: "message",
createdAt: "2026-01-01T00:00:00Z",
message: {
id: "user-1" as never,
role: "user",
text: "Do the thing",
turnId: null,
createdAt: "2026-01-01T00:00:00Z",
streaming: false,
},
},
{
id: "assistant-entry",
kind: "message",
createdAt: "2026-01-01T00:00:20Z",
message: {
id: "assistant-1" as never,
role: "assistant",
text: "Done",
turnId: "turn-1" as never,
createdAt: "2026-01-01T00:00:20Z",
completedAt: "2026-01-01T00:00:30Z",
streaming: false,
},
},
],
completionDividerBeforeEntryId: null,
isWorking: false,
activeTurnStartedAt: null,
turnDiffSummaryByAssistantMessageId: new Map([
["assistant-1" as never, assistantTurnDiffSummary],
]),
revertTurnCountByUserMessageId: new Map([["user-1" as never, 1]]),
});
const userRow = rows.find(
(row): row is Extract<(typeof rows)[number], { kind: "message" }> =>
row.kind === "message" && row.message.role === "user",
);
const assistantRow = rows.find(
(row): row is Extract<(typeof rows)[number], { kind: "message" }> =>
row.kind === "message" && row.message.role === "assistant",
);
expect(userRow?.revertTurnCount).toBe(1);
expect(assistantRow?.assistantTurnDiffSummary).toBe(assistantTurnDiffSummary);
});
});
describe("computeStableMessagesTimelineRows", () => {
it("returns the previous result when row order and content are unchanged", () => {
const firstUserMessage = {
id: "user-1" as never,
role: "user" as const,
text: "First",
turnId: null,
createdAt: "2026-01-01T00:00:00Z",
streaming: false,
};
const secondUserMessage = {
id: "user-2" as never,
role: "user" as const,
text: "Second",
turnId: null,
createdAt: "2026-01-01T00:00:10Z",
streaming: false,
};
const rows = deriveMessagesTimelineRows({
timelineEntries: [
{
id: "entry-user-1",
kind: "message",
createdAt: firstUserMessage.createdAt,
message: firstUserMessage,
},
{
id: "entry-user-2",
kind: "message",
createdAt: secondUserMessage.createdAt,
message: secondUserMessage,
},
],
completionDividerBeforeEntryId: null,
isWorking: false,
activeTurnStartedAt: null,
turnDiffSummaryByAssistantMessageId: new Map(),
revertTurnCountByUserMessageId: new Map(),
});
const initial = computeStableMessagesTimelineRows(rows, {
byId: new Map(),
result: [],
});
const repeated = computeStableMessagesTimelineRows(rows, initial);
expect(repeated).toBe(initial);
expect(repeated.result).toBe(initial.result);
});
it("reuses work rows when equivalent timeline derivations create new grouped arrays", () => {
const firstWorkEntry = {
id: "work-1",
createdAt: "2026-01-01T00:00:00Z",
label: "thinking",
detail: "Inspecting repository state",
tone: "thinking" as const,
};
const secondWorkEntry = {
id: "work-2",
createdAt: "2026-01-01T00:00:01Z",
label: "read",
detail: "Reading package.json",
tone: "tool" as const,
};
const createRows = () =>
deriveMessagesTimelineRows({
timelineEntries: [
{
id: "entry-work-1",
kind: "work",
createdAt: firstWorkEntry.createdAt,
entry: firstWorkEntry,
},
{
id: "entry-work-2",
kind: "work",
createdAt: secondWorkEntry.createdAt,
entry: secondWorkEntry,
},
],
completionDividerBeforeEntryId: null,
isWorking: false,
activeTurnStartedAt: null,
turnDiffSummaryByAssistantMessageId: new Map(),
revertTurnCountByUserMessageId: new Map(),
});
const firstRows = createRows();
const initial = computeStableMessagesTimelineRows(firstRows, {
byId: new Map(),
result: [],
});
const secondRows = createRows();
expect(secondRows[0]).not.toBe(firstRows[0]);
const repeated = computeStableMessagesTimelineRows(secondRows, initial);
expect(repeated).toBe(initial);
expect(repeated.result[0]).toBe(initial.result[0]);
});
it("returns a new result when row order changes without content changes", () => {
const firstUserMessage = {
id: "user-1" as never,
role: "user" as const,
text: "First",
turnId: null,
createdAt: "2026-01-01T00:00:00Z",
streaming: false,
};
const secondUserMessage = {
id: "user-2" as never,
role: "user" as const,
text: "Second",
turnId: null,
createdAt: "2026-01-01T00:00:10Z",
streaming: false,
};
const firstRows = deriveMessagesTimelineRows({
timelineEntries: [
{
id: "entry-user-1",
kind: "message",
createdAt: firstUserMessage.createdAt,
message: firstUserMessage,
},
{
id: "entry-user-2",
kind: "message",
createdAt: secondUserMessage.createdAt,
message: secondUserMessage,
},
],
completionDividerBeforeEntryId: null,
isWorking: false,
activeTurnStartedAt: null,
turnDiffSummaryByAssistantMessageId: new Map(),
revertTurnCountByUserMessageId: new Map(),
});
const initial = computeStableMessagesTimelineRows(firstRows, {
byId: new Map(),
result: [],
});
const reordered = computeStableMessagesTimelineRows([firstRows[1]!, firstRows[0]!], initial);
expect(reordered).not.toBe(initial);
expect(reordered.result).toEqual([initial.result[1], initial.result[0]]);
});
});