fix(app): preserve paginated timeline order (#38641)
This commit is contained in:
parent
a48912cbb1
commit
55f4a2691a
5 changed files with 69 additions and 24 deletions
|
|
@ -263,7 +263,7 @@ describe("server session", () => {
|
|||
expect(store.data.session_message.root.map((message) => message.id)).toEqual([user.id, assistant.id])
|
||||
})
|
||||
|
||||
test("reprojects current assistants when an older page supplies their user", async () => {
|
||||
test("extends a current page to include the user for split assistant turns", async () => {
|
||||
const user = { id: "msg_1_user", type: "user", text: "hello", time: { created: 1 } } as const
|
||||
const assistant = (id: string, created: number) => ({
|
||||
id,
|
||||
|
|
@ -282,17 +282,22 @@ describe("server session", () => {
|
|||
{ data: assistants.slice(1).toReversed(), cursor: { previous: null, next: "older" } },
|
||||
{ data: [assistants[0], user], cursor: { previous: null, next: null } },
|
||||
]
|
||||
const requests: unknown[] = []
|
||||
const messageApi = {
|
||||
list: async () => pages.shift()!,
|
||||
list: async (input: unknown) => {
|
||||
requests.push(input)
|
||||
return pages.shift()!
|
||||
},
|
||||
} as unknown as MessageApi
|
||||
const store = createServerSession({} as OpencodeClient, {} as SessionApi, messageApi)
|
||||
store.remember(session("root"))
|
||||
|
||||
await store.sync("root")
|
||||
expect(store.data.message.root).toEqual([])
|
||||
|
||||
await store.history.loadMore("root")
|
||||
|
||||
expect(requests).toEqual([
|
||||
{ sessionID: "root", limit: 20, order: "desc" },
|
||||
{ sessionID: "root", limit: 20, cursor: "older" },
|
||||
])
|
||||
expect(store.data.message.root.map((message) => message.id)).toEqual([
|
||||
user.id,
|
||||
...assistants.map((item) => item.id),
|
||||
|
|
|
|||
|
|
@ -30,6 +30,17 @@ const historyMessagePageSize = 200
|
|||
const sessionInfoLimit = 2_048
|
||||
const emptyIDs: ReadonlySet<string> = new Set()
|
||||
|
||||
function needsOlderTurnRoot(source: readonly SessionMessageInfo[]) {
|
||||
const boundary = source.find(
|
||||
(message) =>
|
||||
message.type === "user" ||
|
||||
message.type === "shell" ||
|
||||
message.type === "assistant" ||
|
||||
(message.type === "synthetic" && message.description?.trim()),
|
||||
)
|
||||
return boundary?.type === "assistant"
|
||||
}
|
||||
|
||||
type OptimisticItem = {
|
||||
message: Message
|
||||
parts: Part[]
|
||||
|
|
@ -525,11 +536,20 @@ export function createServerSession(
|
|||
|
||||
const fetchMessages = async (sessionID: string, limit: number, before?: string, onAttempt?: () => void) => {
|
||||
if (messageApi && (await options?.protocol) !== "v1") {
|
||||
const response = await (options?.retry ?? retry)(() => {
|
||||
onAttempt?.()
|
||||
return messageApi.list(before ? { sessionID, limit, cursor: before } : { sessionID, limit, order: "desc" })
|
||||
})
|
||||
const source = [...response.data].reverse()
|
||||
const request = (cursor?: string) =>
|
||||
(options?.retry ?? retry)(() => {
|
||||
onAttempt?.()
|
||||
return messageApi.list(cursor ? { sessionID, limit, cursor } : { sessionID, limit, order: "desc" })
|
||||
})
|
||||
const first = await request(before)
|
||||
const pages = [first]
|
||||
while (pages.at(-1)?.cursor.next && needsOlderTurnRoot(pages.flatMap((page) => page.data).toReversed())) {
|
||||
const response = await request(pages.at(-1)!.cursor.next ?? undefined)
|
||||
pages.push(response)
|
||||
if (!response.data.length) break
|
||||
}
|
||||
const response = pages.at(-1)!
|
||||
const source = pages.flatMap((page) => page.data).toReversed()
|
||||
const normalized = normalizeSessionMessages(sessionID, source)
|
||||
return {
|
||||
session: normalized.messages.sort((a, b) => cmp(a.id, b.id)),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue