History

[codex] fix: tolerate invalid latest user message timestamps (#3521)

Wout Stiens committed 3 months ago

Commit 43e2aea1 parent b655a9f2 files · +75 −1Browse files

Unified · Split

2 files
  1. packages/client-runtime/src/state/threadSort.test.ts +71 −0
  2. packages/client-runtime/src/state/threadSort.ts +4 −1
packages/client-runtime/src/state/threadSort.test.ts+71−0
@@ -0,0 +1,71 @@
1+import { describe, expect, it } from "vite-plus/test";
2+
3+import { sortThreads, type ThreadSortInput } from "./threadSort.ts";
4+
5+type TestThread = { readonly id: string } & ThreadSortInput;
6+
7+function makeThread(overrides: Partial<TestThread> = {}): TestThread {
8+ return {
9+ id: "thread-1",
10+ createdAt: "2026-03-09T10:00:00.000Z",
11+ updatedAt: "2026-03-09T10:00:00.000Z",
12+ messages: [],
13+ latestUserMessageAt: null,
14+ ...overrides,
15+ };
16+}
17+
18+describe("sortThreads", () => {
19+ it("falls back to updatedAt and createdAt when latestUserMessageAt is invalid and there are no messages", () => {
20+ const sorted = sortThreads(
21+ [
22+ makeThread({
23+ id: "thread-1",
24+ latestUserMessageAt: "not-a-date",
25+ createdAt: "2026-03-09T10:00:00.000Z",
26+ updatedAt: "2026-03-09T10:05:00.000Z",
27+ }),
28+ makeThread({
29+ id: "thread-2",
30+ latestUserMessageAt: "still-not-a-date",
31+ createdAt: "invalid-created-at",
32+ updatedAt: "invalid-updated-at",
33+ }),
34+ makeThread({
35+ id: "thread-3",
36+ latestUserMessageAt: "invalid-latest-user-message-at",
37+ createdAt: "2026-03-09T10:06:00.000Z",
38+ updatedAt: "invalid-updated-at",
39+ }),
40+ ],
41+ "updated_at",
42+ );
43+
44+ expect(sorted.map((thread) => thread.id)).toEqual(["thread-3", "thread-1", "thread-2"]);
45+ });
46+
47+ it("falls back to the latest valid user message when latestUserMessageAt is invalid", () => {
48+ const sorted = sortThreads(
49+ [
50+ makeThread({
51+ id: "thread-1",
52+ latestUserMessageAt: "invalid-latest-user-message-at",
53+ updatedAt: "2026-03-09T10:00:00.000Z",
54+ messages: [
55+ { role: "user", createdAt: "2026-03-09T10:05:00.000Z" },
56+ { role: "assistant", createdAt: "2026-03-09T10:30:00.000Z" },
57+ { role: "user", createdAt: "2026-03-09T10:20:00.000Z" },
58+ ],
59+ }),
60+ makeThread({
61+ id: "thread-2",
62+ createdAt: "2026-03-09T10:15:00.000Z",
63+ updatedAt: "2026-03-09T10:15:00.000Z",
64+ }),
65+ ],
66+ "updated_at",
67+ );
68+
69+ expect(sorted.map((thread) => thread.id)).toEqual(["thread-1", "thread-2"]);
70+ });
71+});
packages/client-runtime/src/state/threadSort.ts+4−1
@@ -32,7 +32,10 @@ function getFirstSortableTimestamp(...values: Array<string | null | undefined>): · 31 unmodified lines
3232
3333 function getLatestUserMessageTimestamp(thread: ThreadSortInput): number {
3434 if (thread.latestUserMessageAt) {
35− return toSortableTimestamp(thread.latestUserMessageAt) ?? Number.NEGATIVE_INFINITY;
35+ const latestUserMessageTimestamp = toSortableTimestamp(thread.latestUserMessageAt);
36+ if (latestUserMessageTimestamp !== null) {
37+ return latestUserMessageTimestamp;
38+ }
3639 }
3740
3841 let latestUserMessageTimestamp: number | null = null;