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
@@ -122,6 +122,37 @@ describe("materializeSessionSnapshots", () => {
expect(result.part.msg_1).toEqual([serverPart])
})
test("preserves state.time from existing part when snapshot drops it", () => {
const livePart = {
id: "prt_1",
messageID: "msg_1",
sessionID: "ses_1",
type: "tool",
state: { status: "completed", time: { start: 1000, end: 2000 } },
} as unknown as Part
const snapshotPart = {
id: "prt_1",
messageID: "msg_1",
sessionID: "ses_1",
type: "tool",
state: { status: "completed" },
} as unknown as Part
const state = {
message: { ses_1: [message("msg_1")] },
part: { msg_1: [livePart] },
}
const result = materializeSessionSnapshots(
state,
"ses_1",
[{ info: message("msg_1"), parts: [snapshotPart] }],
)
const mergedPart = result.part.msg_1[0] as { state?: { time?: { start?: number; end?: number } } }
expect(mergedPart.state?.time?.start).toBe(1000)
expect(mergedPart.state?.time?.end).toBe(2000)
})
})
describe("getSessionMaterializationStatus", () => {