Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3e0e477
feat(persistence): add ai-persistence + drizzle/prisma/cloudflare bac…
AlemTuzlak Jul 22, 2026
29382a9
feat(ai-client): browser-refresh durability via the persistence option
AlemTuzlak Jul 22, 2026
0ed55b8
example(ts-react-chat): persistent chat with SQLite + reload durability
AlemTuzlak Jul 22, 2026
01bc50e
docs(persistence): add persistence section for server + browser durab…
AlemTuzlak Jul 22, 2026
2a92c33
docs(skills): cover persistence in middleware + chat-experience skills
AlemTuzlak Jul 22, 2026
c33dc5d
test(e2e): persistence browser-refresh durability spec
AlemTuzlak Jul 22, 2026
b443a9f
ci: apply automated fixes
autofix-ci[bot] Jul 22, 2026
89932a8
feat(ai-client): message-storage lever for the persistence option
AlemTuzlak Jul 23, 2026
e8aef35
docs(persistence): explain durability + persistence as one story
AlemTuzlak Jul 23, 2026
a14adc6
feat(ai-client): zero-config storage adapters + threadId-keyed persis…
AlemTuzlak Jul 23, 2026
a5ca7d2
feat(ai-persistence): reconstructChat server helper
AlemTuzlak Jul 23, 2026
ac68bf7
docs(persistence): recommended setup, zero-config adapters, header-ac…
AlemTuzlak Jul 23, 2026
e5c4064
ci: apply automated fixes
autofix-ci[bot] Jul 23, 2026
8f408cb
docs(persistence): consolidate client persistence into one page
AlemTuzlak Jul 23, 2026
57aafee
docs(persistence): lead client-persistence with both use-cases
AlemTuzlak Jul 23, 2026
08d2ea3
fix(ai-client): keep host initialMessages in messages:false mode
AlemTuzlak Jul 23, 2026
cea0a83
ci: apply automated fixes
autofix-ci[bot] Jul 23, 2026
caf0990
feat(ai-persistence)!: rename withChatPersistence to withPersistence
AlemTuzlak Jul 23, 2026
f2eeba6
ci: apply automated fixes
autofix-ci[bot] Jul 23, 2026
462e15c
fix(persistence): reconcile with current @tanstack/ai core
AlemTuzlak Jul 23, 2026
2f2ab05
test(persistence): unskip two-phase approval->client-tool resume
AlemTuzlak Jul 23, 2026
15940bb
docs(example): ts-react-chat persistent-chat uses recommended server-…
AlemTuzlak Jul 23, 2026
978996e
feat(persistence-prisma): support Prisma 7 alongside Prisma 6
AlemTuzlak Jul 23, 2026
ad61ff5
fix(persistence): persist the terminal assistant reply on finish
AlemTuzlak Jul 23, 2026
c80ff23
docs(example): add server tools and a styled UI to persistent-chat
AlemTuzlak Jul 23, 2026
d828fb9
feat(persistence): client-side generation persistence
AlemTuzlak Jul 23, 2026
dfdb40b
docs(persistence): drop generic from generation snapshot store example
AlemTuzlak Jul 23, 2026
d41e6e6
refactor(persistence): align generation persistence API with chat
AlemTuzlak Jul 23, 2026
c6709d7
refactor(persistence): infer generation store type via GenerationPers…
AlemTuzlak Jul 23, 2026
e6e0431
refactor(persistence): value-agnostic web-storage adapter defaults
AlemTuzlak Jul 23, 2026
d874ab1
docs(persistence): fix stale generation-persistence delivery guidance
AlemTuzlak Jul 23, 2026
89ea1ae
docs(persistence): rewrite generation-persistence for clarity + when-…
AlemTuzlak Jul 23, 2026
d7359e7
docs(persistence): drop redundant storage-adapter comment
AlemTuzlak Jul 23, 2026
c7a901f
feat(persistence): durable generation media-byte storage
AlemTuzlak Jul 23, 2026
f3d9fe6
docs(persistence): document generation media-byte storage (artifacts …
AlemTuzlak Jul 23, 2026
11fad30
feat(ai-persistence): retrieveArtifact / retrieveBlob serve helpers
AlemTuzlak Jul 23, 2026
41397c1
docs(persistence): clarify the artifact serve route fetches files, no…
AlemTuzlak Jul 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/client-browser-refresh-durability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@tanstack/ai-client': minor
'@tanstack/ai-react': minor
'@tanstack/ai-solid': minor
'@tanstack/ai-vue': minor
'@tanstack/ai-svelte': minor
'@tanstack/ai-angular': minor
'@tanstack/ai-preact': minor
---

Add browser-refresh durability to the `persistence` option.

The client `persistence` adapter now stores one combined record per chat id, the message transcript plus a resume snapshot, so a full page reload restores the conversation, rehydrates any pending interrupt, and rejoins a run that was still streaming (via `joinRun`, when the connection is durability-backed). A bare `UIMessage[]` from an older store is still read for backward compatibility.

The `persistence` option also accepts an object form, `{ store, messages?: boolean }`. `messages: false` caches only the tiny resume pointer (which run to rejoin, which interrupts are pending), keeping large transcripts off the client, durability rejoin and interrupt restore still work, and the server stays authoritative for history. A bare adapter is shorthand for `{ store, messages: true }`.

New web storage adapters are exported for this: `localStoragePersistence`, `sessionStoragePersistence`, and `indexedDBPersistence` (plus `StorageUnavailableError` and the `ChatPersistedState` / `ChatStorageAdapter` / `ChatPersistenceConfig` / `ChatPersistenceOption` types). Because durability rides the existing `persistence` option, every framework integration (`react`, `solid`, `vue`, `svelte`, `angular`, `preact`) gets it with no framework-specific code.
22 changes: 22 additions & 0 deletions .changeset/generation-persistence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
'@tanstack/ai': minor
'@tanstack/ai-utils': minor
'@tanstack/ai-persistence': minor
'@tanstack/ai-client': minor
'@tanstack/ai-event-client': minor
'@tanstack/ai-react': minor
'@tanstack/ai-solid': minor
'@tanstack/ai-vue': minor
'@tanstack/ai-svelte': minor
'@tanstack/ai-angular': minor
---

Add generation persistence: a lightweight client resume snapshot plus optional durable storage of the generated media bytes.

**Media byte storage.** `withGenerationPersistence` now persists the generated bytes when the persistence backend provides both an `artifacts` (`ArtifactStore`) and a `blobs` (`BlobStore`) store. As an image/audio/speech/transcription run finishes, the middleware writes each artifact's bytes to the blob store (key `artifacts/<runId>/<artifactId>`), records an `ArtifactRecord`, attaches `PersistedArtifactRef`s to the result, and emits a `generation:artifacts` event so the client records them. Extraction is customizable via `extractArtifacts` / `nameArtifact`. `memoryPersistence()` ships in-memory `artifacts`/`blobs` stores; the generation activities gained `threadId` / `runId` options and run their result through middleware result transforms. `@tanstack/ai-utils` adds `base64ToUint8Array`. To serve a stored artifact, `@tanstack/ai-persistence` exports `retrieveArtifact(persistence, id)` and `retrieveBlob(persistence, idOrRecord)` (plus `artifactBlobKey`).

Add client-side generation persistence: a lightweight, read-only resume snapshot for media generation activities.

Generation hooks (`useGenerateImage`, `useGenerateVideo`, `useGenerateAudio`, `useGenerateSpeech`, `useGeneration`, `useSummarize`, `useTranscription`, and their Solid/Vue/Svelte/Angular equivalents) now accept a `persistence` storage adapter and an `initialResumeSnapshot`, and expose `resumeSnapshot` / `resumeState` (plus observed `pendingArtifacts` / `resultArtifacts`). As a run streams, the client builds a `GenerationResumeSnapshot` — run identity, status, errors, and result metadata, but **never** the generated media bytes — and writes it to the adapter. The `persistence` option reuses the same `ChatStorageAdapter` contract as chat, so the shared `localStoragePersistence` / `sessionStoragePersistence` / `indexedDBPersistence` factories work for generations too. On reload the snapshot is surfaced for observability; it exposes no `resume()` action and never restarts provider work — generation still only begins when `generate(...)` is called.

This pairs with the existing `withGenerationPersistence` server middleware, which records run status in the shared `RunStore`.
19 changes: 19 additions & 0 deletions .changeset/persistence-packages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'@tanstack/ai-persistence': minor
'@tanstack/ai-persistence-drizzle': minor
'@tanstack/ai-persistence-prisma': minor
'@tanstack/ai-persistence-cloudflare': minor
---

Add server-side persistence for `chat()`: durable thread messages, run records, and interrupts.

`withPersistence(persistence)` is a chat middleware that stores the conversation transcript, tracks each run's status, and records interrupt state so a paused run (tool approval, client-tool execution, generic interrupt) survives a server restart. Point it at any backend that implements the store contract:

- `@tanstack/ai-persistence` — the store contracts, the `withPersistence` / `withGenerationPersistence` middleware, and an in-memory reference store (`memoryPersistence`) plus a conformance testkit.
- `@tanstack/ai-persistence-drizzle` — Drizzle-backed stores (SQLite via `node:sqlite`, plus a bring-your-own-schema contract) with migration and schema CLIs.
- `@tanstack/ai-persistence-prisma` — Prisma-backed stores with a models CLI that emits a provider-neutral schema fragment. Works with both Prisma 6 (`prisma-client-js`) and Prisma 7 (`prisma-client`): the client argument is typed structurally, so it accepts a client generated to any output path.
- `@tanstack/ai-persistence-cloudflare` — D1-backed stores (delegating to the Drizzle backend) plus a Durable-Object lock store for cross-instance locking.

Resume reconstruction is delegated to the chat engine: persistence records interrupts and gates new input on a thread with pending interrupts, while the engine rebuilds the resume tool state from the resume batch and the interrupt bindings carried in the (server-loaded) message history.

`reconstructChat(persistence, request)` is a server helper that returns a thread's stored messages as a JSON `Response`, so a server-authoritative client can hydrate its transcript on load from a one-line `GET` handler.
153 changes: 0 additions & 153 deletions docs/chat/persistence.md

This file was deleted.

78 changes: 71 additions & 7 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,6 @@
"label": "Thinking & Reasoning",
"to": "chat/thinking-content",
"addedAt": "2026-04-15"
},
{
"label": "Persistence",
"to": "chat/persistence",
"addedAt": "2026-06-02",
"updatedAt": "2026-07-14"
}
]
},
Expand Down Expand Up @@ -224,7 +218,8 @@
{
"label": "Overview",
"to": "resumable-streams/overview",
"addedAt": "2026-07-17"
"addedAt": "2026-07-17",
"updatedAt": "2026-07-23"
},
{
"label": "Advanced",
Expand All @@ -238,6 +233,75 @@
}
]
},
{
"label": "Persistence",
"children": [
{
"label": "Overview",
"to": "persistence/overview",
"addedAt": "2026-07-22",
"updatedAt": "2026-07-23"
},
{
"label": "Chat Persistence",
"to": "persistence/chat-persistence",
"addedAt": "2026-07-22"
},
{
"label": "Client Persistence",
"to": "persistence/client-persistence",
"addedAt": "2026-07-22",
"updatedAt": "2026-07-23"
},
{
"label": "Generation Persistence",
"to": "persistence/generation-persistence",
"addedAt": "2026-07-23"
},
{
"label": "Controls",
"to": "persistence/controls",
"addedAt": "2026-07-22",
"updatedAt": "2026-07-23"
},
{
"label": "Custom Stores",
"to": "persistence/custom-stores",
"addedAt": "2026-07-22"
},
{
"label": "SQL Backends",
"to": "persistence/sql-backends",
"addedAt": "2026-07-22"
},
{
"label": "Drizzle",
"to": "persistence/drizzle",
"addedAt": "2026-07-22"
},
{
"label": "Prisma",
"to": "persistence/prisma",
"addedAt": "2026-07-22",
"updatedAt": "2026-07-23"
},
{
"label": "Cloudflare",
"to": "persistence/cloudflare",
"addedAt": "2026-07-22"
},
{
"label": "Migrations",
"to": "persistence/migrations",
"addedAt": "2026-07-22"
},
{
"label": "Internals",
"to": "persistence/internals",
"addedAt": "2026-07-22"
}
]
},
{
"label": "Protocol",
"children": [
Expand Down
Loading
Loading