Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions .changeset/persistence-session-stores.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'@tanstack/ai-persistence': minor
---

Add opt-in session stores. The chat and generation middleware work as before without them.

- `inbox` (`InboxStore`): the inputs a host accepted, with an idempotent `append` by `inputId`, `listPending`, `markApplied`, `markRejected`, and `get`.
- `credentials` (`CredentialStore`): API keys and OAuth tokens by scope and id. An `oauth` credential can keep the OAuth `client` it was issued to. `list` never returns secret values.
- `log` (`LogStore`): an append-only log per thread. `append` writes a batch at a position, all or nothing, and rejects with `LogConflictError` when another writer took that position. `read` and `subscribe` complete it. `memoryLogStore()` is the reference store. It is not part of `memoryPersistence()`.
- `leases` (`LeaseStore`, `TurnLease`, `TurnLeaseKey`): turn leases from a system that already has them.
- `workClaims` (`WorkClaimStore`): claims on threads that have pending work, with `claim`, `release`, and `listExpired`.
- `sessions` (`SessionIndexStore`): one index entry per thread, with `upsert`, `get`, `list`, and `delete`. `list` filters by parent thread, owner, title `search`, `harness` name, and exact `metadata` values, and pages with a cursor. The optional `getVersioned` and `upsertIf` give compare-and-set writes.

`defineInboxStore`, `defineCredentialStore`, `defineLogStore`, `defineWorkClaimStore`, and `defineSessionIndexStore` type an adapter. `memoryPersistence()` now also has `inbox`, `credentials`, `workClaims`, and `sessions`. `runPersistenceConformance` runs the cases of each session store when the store is present, so an existing backend needs no `skip` entry.

With `stores.sessions`, `withPersistence` writes one index entry for each subagent child (`subagent:<runId>`), with the parent thread, the tool call, and the owner and `harness` name of the parent entry.
2 changes: 1 addition & 1 deletion docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@
"label": "Build Your Own Adapter",
"to": "persistence/build-your-own-adapter",
"addedAt": "2026-08-04",
"updatedAt": "2026-09-29"
"updatedAt": "2026-10-13"
},
{
"label": "Migrations",
Expand Down
5 changes: 5 additions & 0 deletions docs/persistence/build-your-own-adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ The optional run methods are `listByThread`, `listByParentRun`, and
`skipMethods`. An omitted `listByParentRun` needs no entry: the subagent checks
skip on their own.

The session stores (`inbox`, `credentials`, `log`, `workClaims`, `sessions`) are
opt-in too. Their cases run only when your persistence has the store, so a backend
without them needs no `skip` entry. See the
[session stores](./store-reference#session-stores) for their rules.

### Turn on the newer checks

Some checks were added after the suite shipped. They are off by default, so an
Expand Down
Loading
Loading