fix(chat): preserve tool duration across session switches (#1712)

* fix(chat): preserve tool duration across session switches

Fix #1636: ToolPart.tsx reset pinnedTime to empty on unmount/remount,
causing LiveDuration to not render on first paint. Now initializes
pinnedTime from server-provided time?.start/time?.end in the useState
initializer, eliminating the one-frame gap.

* fix(sync): preserve tool state.time in materialization merge

---------

Co-authored-by: Leonid Skorobogatyy <bash@opencode.itc.local>
This commit is contained in:
bashrusakh
2026-06-23 22:30:04 +03:00
committed by GitHub
co-authored by Leonid Skorobogatyy
parent 57cef1b278
commit ac0f173655
3 changed files with 58 additions and 1 deletions
+23
View File
@@ -77,6 +77,15 @@ function hasLiveStreamingField(part: Part): boolean {
})
}
function getPartStateTime(part: Part): { start?: number; end?: number } | undefined {
const stateTime = (part as { state?: { time?: { start?: unknown; end?: unknown } } }).state?.time
if (!stateTime || typeof stateTime !== "object") return undefined
const start = typeof stateTime.start === "number" ? stateTime.start : undefined
const end = typeof stateTime.end === "number" ? stateTime.end : undefined
if (start === undefined && end === undefined) return undefined
return { start, end }
}
function mergeMaterializedPart(existing: Part | undefined, next: Part): Part {
if (!existing || getPartEndTime(next) !== undefined) return next
@@ -94,6 +103,20 @@ function mergeMaterializedPart(existing: Part | undefined, next: Part): Part {
mergedRecord[field] = existingValue
}
const existingTime = getPartStateTime(existing)
if (existingTime) {
const nextTime = getPartStateTime(next)
const preservedStart = nextTime?.start ?? existingTime.start
const preservedEnd = nextTime?.end ?? existingTime.end
if (preservedStart !== nextTime?.start || preservedEnd !== nextTime?.end) {
if (merged === next) merged = { ...next }
const mergedRecord = merged as Record<string, unknown>
const nextState = (next as Record<string, unknown>).state as Record<string, unknown> | undefined
const newState = { ...(nextState ?? {}), time: { start: preservedStart, end: preservedEnd } }
mergedRecord.state = newState
}
}
return merged
}