@tanstack/ai-persistence 0.5.5 → 0.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md +11 -0
- package/skills/ai-persistence/build-custom-adapter/SKILL.md +2 -2
- package/skills/ai-persistence/build-drizzle-adapter/SKILL.md +1 -1
- package/skills/ai-persistence/build-prisma-adapter/SKILL.md +1 -1
- package/skills/ai-persistence/server/SKILL.md +5 -0
- package/skills/ai-persistence/stores/SKILL.md +40 -22
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-persistence",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
4
4
|
"description": "Composable state persistence for TanStack AI messages, runs, interrupts, metadata, and locks.",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"vitest": "^4.1.10",
|
|
46
|
-
"@tanstack/ai": "^0.
|
|
46
|
+
"@tanstack/ai": "^0.54.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependenciesMeta": {
|
|
49
49
|
"vitest": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@vitest/coverage-v8": "4.1.10",
|
|
55
55
|
"vitest": "^4.1.10",
|
|
56
|
-
"@tanstack/ai": "0.
|
|
56
|
+
"@tanstack/ai": "0.54.0"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "vite build",
|
|
@@ -28,6 +28,17 @@ type an object literal inline (autocomplete + contract checking, no separate
|
|
|
28
28
|
annotation).
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
|
+
import type {
|
|
32
|
+
ArtifactRecord,
|
|
33
|
+
BlobBody,
|
|
34
|
+
BlobGetOptions,
|
|
35
|
+
BlobListOptions,
|
|
36
|
+
BlobListPage,
|
|
37
|
+
BlobObject,
|
|
38
|
+
BlobPutOptions,
|
|
39
|
+
BlobRecord,
|
|
40
|
+
} from '@tanstack/ai-persistence'
|
|
41
|
+
|
|
31
42
|
// BlobStore — the byte layer. R2 backs it.
|
|
32
43
|
interface BlobStore {
|
|
33
44
|
put: (
|
|
@@ -259,7 +259,7 @@ inspects your storage.
|
|
|
259
259
|
You rarely need all four stores at once. Implement what you own and fill the
|
|
260
260
|
rest from another base:
|
|
261
261
|
|
|
262
|
-
```ts
|
|
262
|
+
```ts
|
|
263
263
|
import { composePersistence, memoryPersistence } from '@tanstack/ai-persistence'
|
|
264
264
|
import { messages, runs } from './my-stores'
|
|
265
265
|
|
|
@@ -307,7 +307,7 @@ This matters more here than anywhere else: there is no reference driver to
|
|
|
307
307
|
compare against, so the testkit is the only thing standing between a subtle
|
|
308
308
|
idempotency bug and stuck approvals in production.
|
|
309
309
|
|
|
310
|
-
```ts
|
|
310
|
+
```ts
|
|
311
311
|
import { runPersistenceConformance } from '@tanstack/ai-persistence/testkit'
|
|
312
312
|
import { chatPersistence } from '../src/lib/chat-persistence'
|
|
313
313
|
|
|
@@ -516,7 +516,7 @@ route** — derive the user from the session, never trust a client-supplied id.
|
|
|
516
516
|
|
|
517
517
|
## 5. Verify
|
|
518
518
|
|
|
519
|
-
```ts
|
|
519
|
+
```ts
|
|
520
520
|
import { runPersistenceConformance } from '@tanstack/ai-persistence/testkit'
|
|
521
521
|
import { chatPersistence } from '../src/lib/chat-persistence'
|
|
522
522
|
|
|
@@ -474,7 +474,7 @@ route** — derive the user from the session, never trust a client-supplied id.
|
|
|
474
474
|
|
|
475
475
|
## 5. Verify
|
|
476
476
|
|
|
477
|
-
```ts
|
|
477
|
+
```ts
|
|
478
478
|
import { runPersistenceConformance } from '@tanstack/ai-persistence/testkit'
|
|
479
479
|
import { chatPersistence } from '../src/lib/chat-persistence'
|
|
480
480
|
|
|
@@ -98,6 +98,9 @@ preserve plain-text and structured-output assistant messages separately when
|
|
|
98
98
|
those messages use different ids.
|
|
99
99
|
|
|
100
100
|
```ts
|
|
101
|
+
import { withPersistence } from '@tanstack/ai-persistence'
|
|
102
|
+
import { persistence } from './persistence'
|
|
103
|
+
|
|
101
104
|
withPersistence(persistence, {
|
|
102
105
|
snapshotStreaming: true,
|
|
103
106
|
snapshotIntervalMs: 1000, // default
|
|
@@ -159,6 +162,8 @@ Server-authoritative clients load history by `threadId` (often `GET`):
|
|
|
159
162
|
|
|
160
163
|
```ts
|
|
161
164
|
import { reconstructChat } from '@tanstack/ai-persistence'
|
|
165
|
+
import { persistence } from './persistence'
|
|
166
|
+
import { sessionUserId, userOwnsThread } from './auth'
|
|
162
167
|
|
|
163
168
|
export async function GET(request: Request) {
|
|
164
169
|
return reconstructChat(persistence, request, {
|
|
@@ -37,6 +37,7 @@ a complete `node:sqlite` implementation lives in
|
|
|
37
37
|
```ts
|
|
38
38
|
import { defineAIPersistence } from '@tanstack/ai-persistence'
|
|
39
39
|
import type { ChatWithInterruptsPersistence } from '@tanstack/ai-persistence'
|
|
40
|
+
import { messages, runs, interrupts } from './stores'
|
|
40
41
|
|
|
41
42
|
// Sparse is fine — only implement what you need.
|
|
42
43
|
export const persistence: ChatWithInterruptsPersistence = defineAIPersistence({
|
|
@@ -73,9 +74,11 @@ mistake when writing an adapter.
|
|
|
73
74
|
### `MessageStore`
|
|
74
75
|
|
|
75
76
|
```ts
|
|
77
|
+
import type { ModelMessage } from '@tanstack/ai'
|
|
78
|
+
|
|
76
79
|
interface MessageStore {
|
|
77
|
-
loadThread(threadId: string)
|
|
78
|
-
saveThread(threadId: string, messages: Array<ModelMessage>)
|
|
80
|
+
loadThread: (threadId: string) => Promise<Array<ModelMessage>>
|
|
81
|
+
saveThread: (threadId: string, messages: Array<ModelMessage>) => Promise<void>
|
|
79
82
|
}
|
|
80
83
|
```
|
|
81
84
|
|
|
@@ -120,6 +123,9 @@ always a choice you made on purpose rather than a check that quietly did not
|
|
|
120
123
|
run. Declare yours and the suite reports them as skipped with a reason:
|
|
121
124
|
|
|
122
125
|
```ts
|
|
126
|
+
import { runPersistenceConformance } from '@tanstack/ai-persistence/testkit'
|
|
127
|
+
import { persistence } from './persistence'
|
|
128
|
+
|
|
123
129
|
// The shipped sqlite example implements findActiveRun and listReclaimable and
|
|
124
130
|
// declares only the one it omits.
|
|
125
131
|
runPersistenceConformance('sqlite', () => persistence, {
|
|
@@ -128,14 +134,16 @@ runPersistenceConformance('sqlite', () => persistence, {
|
|
|
128
134
|
```
|
|
129
135
|
|
|
130
136
|
```ts
|
|
137
|
+
import type { RunRecord, RunStatus } from '@tanstack/ai-persistence'
|
|
138
|
+
|
|
131
139
|
interface RunStore {
|
|
132
140
|
// Required
|
|
133
|
-
createOrResume(
|
|
141
|
+
createOrResume: (
|
|
134
142
|
input: Pick<RunRecord, 'runId' | 'threadId' | 'startedAt'> & {
|
|
135
143
|
status?: RunStatus
|
|
136
144
|
},
|
|
137
|
-
)
|
|
138
|
-
update(
|
|
145
|
+
) => Promise<RunRecord>
|
|
146
|
+
update: (
|
|
139
147
|
runId: string,
|
|
140
148
|
patch: Partial<
|
|
141
149
|
Pick<
|
|
@@ -150,16 +158,16 @@ interface RunStore {
|
|
|
150
158
|
| 'driverEpoch'
|
|
151
159
|
>
|
|
152
160
|
>,
|
|
153
|
-
)
|
|
154
|
-
get(runId: string)
|
|
155
|
-
findActiveRun(threadId: string)
|
|
161
|
+
) => Promise<void>
|
|
162
|
+
get: (runId: string) => Promise<RunRecord | null>
|
|
163
|
+
findActiveRun: (threadId: string) => Promise<RunRecord | null>
|
|
156
164
|
|
|
157
165
|
// Optional
|
|
158
|
-
listByThread
|
|
159
|
-
listReclaimable
|
|
166
|
+
listByThread?: (threadId: string) => Promise<Array<RunRecord>>
|
|
167
|
+
listReclaimable?: (opts: {
|
|
160
168
|
now: number
|
|
161
169
|
ttlMs: number
|
|
162
|
-
})
|
|
170
|
+
}) => Promise<Array<RunRecord>>
|
|
163
171
|
}
|
|
164
172
|
```
|
|
165
173
|
|
|
@@ -293,15 +301,25 @@ and each must be declared via `skipMethods` when absent.
|
|
|
293
301
|
### `InterruptStore`
|
|
294
302
|
|
|
295
303
|
```ts
|
|
304
|
+
import type {
|
|
305
|
+
InterruptCommitEntry,
|
|
306
|
+
InterruptRecord,
|
|
307
|
+
} from '@tanstack/ai-persistence'
|
|
308
|
+
|
|
296
309
|
interface InterruptStore {
|
|
297
|
-
create
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
310
|
+
create: (
|
|
311
|
+
record: Omit<InterruptRecord, 'status' | 'resolvedAt'>,
|
|
312
|
+
) => Promise<void>
|
|
313
|
+
resolve: (interruptId: string, response?: unknown) => Promise<void>
|
|
314
|
+
cancel: (interruptId: string) => Promise<void>
|
|
315
|
+
// Optional: apply a validated resume batch all-or-nothing instead of
|
|
316
|
+
// per-entry resolve/cancel.
|
|
317
|
+
commitBatch?: (entries: ReadonlyArray<InterruptCommitEntry>) => Promise<void>
|
|
318
|
+
get: (interruptId: string) => Promise<InterruptRecord | null>
|
|
319
|
+
list: (threadId: string) => Promise<Array<InterruptRecord>>
|
|
320
|
+
listPending: (threadId: string) => Promise<Array<InterruptRecord>>
|
|
321
|
+
listByRun: (runId: string) => Promise<Array<InterruptRecord>>
|
|
322
|
+
listPendingByRun: (runId: string) => Promise<Array<InterruptRecord>>
|
|
305
323
|
}
|
|
306
324
|
```
|
|
307
325
|
|
|
@@ -314,9 +332,9 @@ interface InterruptStore {
|
|
|
314
332
|
|
|
315
333
|
```ts
|
|
316
334
|
interface MetadataStore {
|
|
317
|
-
get(namespace: string, key: string)
|
|
318
|
-
set(namespace: string, key: string, value: unknown)
|
|
319
|
-
delete(namespace: string, key: string)
|
|
335
|
+
get: (namespace: string, key: string) => Promise<unknown | null>
|
|
336
|
+
set: (namespace: string, key: string, value: unknown) => Promise<void>
|
|
337
|
+
delete: (namespace: string, key: string) => Promise<void>
|
|
320
338
|
}
|
|
321
339
|
```
|
|
322
340
|
|