fix(chat): keep messages chronological across ID rollover

This commit is contained in:
Bohdan Triapitsyn
2026-08-14 16:53:05 +03:00
parent 7cf869d5eb
commit fe1f6130d6
23 changed files with 405 additions and 132 deletions
+11 -9
View File
@@ -1,8 +1,8 @@
import type { Message, Part } from "@opencode-ai/sdk/v2/client"
import { mergeMessages } from "./optimistic"
import type { SessionMaterializationReason } from "./event-reducer"
import { sortMessagesChronologically } from "./message-ordering"
const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
const STREAMING_PART_FIELDS = ["text", "output"] as const
const ACTIVE_TOOL_STATUSES = new Set(["pending", "running"])
const FINAL_TOOL_STATUSES = new Set(["completed", "error", "aborted", "failed", "timeout", "cancelled"])
@@ -93,10 +93,9 @@ export function getStaleRunningToolMessageID(
return undefined
}
function sortParts(parts: Part[], skipPartTypes: ReadonlySet<string>) {
function filterMaterializedParts(parts: Part[], skipPartTypes: ReadonlySet<string>): Part[] {
return parts
.filter((part) => !!part?.id && !skipPartTypes.has(part.type))
.sort((a, b) => cmp(a.id, b.id))
}
function haveEquivalentPartSnapshots(left: Part[] | undefined, right: Part[]): boolean {
@@ -252,7 +251,7 @@ function mergeMaterializedParts(
)
if (missingLiveParts.length === 0) return mergedParts
return [...mergedParts, ...missingLiveParts].sort((a, b) => cmp(a.id, b.id))
return [...mergedParts, ...missingLiveParts]
}
export function materializeSessionSnapshots(
@@ -262,10 +261,13 @@ export function materializeSessionSnapshots(
options: MaterializeSessionSnapshotsOptions = {},
): MaterializeSessionSnapshotsResult {
const skipPartTypes = options.skipPartTypes ?? new Set<string>()
const snapshots = records
.filter((record) => !!record?.info?.id)
.sort((left, right) => cmp(left.info.id, right.info.id))
const nextMessages = snapshots.map((record) => record.info)
const recordsByMessageID = new Map(
records
.filter((record) => !!record?.info?.id)
.map((record) => [record.info.id, record] as const),
)
const nextMessages = sortMessagesChronologically([...recordsByMessageID.values()].map((record) => record.info))
const snapshots = nextMessages.map((message) => recordsByMessageID.get(message.id)!)
const existingMessages = state.message[sessionID]
const currentMessages = existingMessages ?? []
const messages = mergeMessages(currentMessages, nextMessages)
@@ -283,7 +285,7 @@ export function materializeSessionSnapshots(
const existing = nextPartState[messageID]
const nextParts = mergeMaterializedParts(
existing,
sortParts(record.parts ?? [], skipPartTypes),
filterMaterializedParts(record.parts ?? [], skipPartTypes),
skipPartTypes,
isAssistant,
)