ai-persistence: expose finished-run timings (startedAt/finishedAt) in reconstructChat
@AlemTuzlak is already working on this.
Since Aug 21, 2026.
- Dominant language
- TypeScript
- Stars
- 3.1k
- Forks
- 331
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 160
Description
Use case
Our chat UI shows a "Worked for Xs" duration label on each assistant turn. Live turns time themselves client-side, but after a reload/rehydrate via reconstructChat + persistence: true there is no way to recover run durations through the library:
ReconstructedChatreturnsmessages(fromMessageStore.loadThread→ModelMessage[], no timestamps) andactiveRunas a bare{ runId }cursor.- Finished runs never reach the client, even though
RunRecordalready carriesstartedAt/finishedAtin theRunStorecontract — the data is sitting in the store the server is already querying.
Today the only options are a parallel app-owned history endpoint (defeats the point of the library owning hydration) or dropping durations on resumed turns (what we chose).
Proposal
Have reconstructChat (optionally?) include the thread's terminal runs — e.g.
interface ReconstructedChat {
messages: Array<UIMessage>
activeRun: { runId: string } | null
runs?: Array<{ runId: string; status: TerminalRunStatus; startedAt: number; finishedAt?: number }>
interrupts: /* … */
}
requiring only a RunStore.listRuns(threadId) (or similar) addition. An opt-in flag on ReconstructChatOptions would keep the default payload unchanged.
Happy to PR this if the shape sounds right.
Versions: @tanstack/ai@0.43.0, @tanstack/ai-persistence@0.1.0, @tanstack/ai-client@0.23.0.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.