@tanstack/ai-persistence 0.0.0
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/dist/esm/blob-range.d.ts +51 -0
- package/dist/esm/blob-range.js +84 -0
- package/dist/esm/blob-range.js.map +1 -0
- package/dist/esm/capabilities.d.ts +5 -0
- package/dist/esm/capabilities.js +16 -0
- package/dist/esm/capabilities.js.map +1 -0
- package/dist/esm/index.d.ts +13 -0
- package/dist/esm/index.js +9 -0
- package/dist/esm/memory.d.ts +19 -0
- package/dist/esm/memory.js +319 -0
- package/dist/esm/memory.js.map +1 -0
- package/dist/esm/middleware.d.ts +252 -0
- package/dist/esm/middleware.js +872 -0
- package/dist/esm/middleware.js.map +1 -0
- package/dist/esm/reconstruct-generation.d.ts +129 -0
- package/dist/esm/reconstruct-generation.js +148 -0
- package/dist/esm/reconstruct-generation.js.map +1 -0
- package/dist/esm/reconstruct.d.ts +79 -0
- package/dist/esm/reconstruct.js +75 -0
- package/dist/esm/reconstruct.js.map +1 -0
- package/dist/esm/retrieve.d.ts +40 -0
- package/dist/esm/retrieve.js +54 -0
- package/dist/esm/retrieve.js.map +1 -0
- package/dist/esm/testkit/conformance.d.ts +33 -0
- package/dist/esm/testkit/conformance.js +997 -0
- package/dist/esm/testkit/conformance.js.map +1 -0
- package/dist/esm/types.d.ts +554 -0
- package/dist/esm/types.js +103 -0
- package/dist/esm/types.js.map +1 -0
- package/package.json +71 -0
- package/skills/ai-persistence/SKILL.md +218 -0
- package/skills/ai-persistence/build-cloudflare-adapter/SKILL.md +313 -0
- package/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md +693 -0
- package/skills/ai-persistence/build-custom-adapter/SKILL.md +328 -0
- package/skills/ai-persistence/build-drizzle-adapter/SKILL.md +562 -0
- package/skills/ai-persistence/build-prisma-adapter/SKILL.md +518 -0
- package/skills/ai-persistence/server/SKILL.md +210 -0
- package/skills/ai-persistence/stores/SKILL.md +485 -0
- package/src/blob-range.ts +101 -0
- package/src/capabilities.ts +18 -0
- package/src/index.ts +114 -0
- package/src/memory.ts +491 -0
- package/src/middleware.ts +1795 -0
- package/src/reconstruct-generation.ts +244 -0
- package/src/reconstruct.ts +149 -0
- package/src/retrieve.ts +77 -0
- package/src/testkit/conformance.ts +1288 -0
- package/src/types.ts +878 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { BlobRange } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve a requested {@link BlobRange} against an object's real size, the way
|
|
4
|
+
* every byte-storing blob store has to before it slices.
|
|
5
|
+
*
|
|
6
|
+
* Clamps `length` to the end of the object and treats an absent `length` as
|
|
7
|
+
* "to the end", so the result is always the slice actually served — which is
|
|
8
|
+
* what `BlobObject.range` reports and what a `206` response's `Content-Range`
|
|
9
|
+
* is built from.
|
|
10
|
+
*
|
|
11
|
+
* Throws on an `offset` outside the object: that is a caller error, not a
|
|
12
|
+
* store error. A serve route knows the size (it is on the artifact record) and
|
|
13
|
+
* answers `416` from it, so a store only ever sees a satisfiable range unless
|
|
14
|
+
* something upstream is wrong — and silently returning an empty body there
|
|
15
|
+
* would serve a `206` that claims bytes it does not carry.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const { offset, length } = resolveBlobRange(bytes.byteLength, range)
|
|
20
|
+
* const slice = bytes.subarray(offset, offset + length)
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveBlobRange(size: number, range: BlobRange): {
|
|
24
|
+
offset: number;
|
|
25
|
+
length: number;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Resolve an HTTP `Range` header against a known object size, for a route that
|
|
29
|
+
* serves artifact bytes.
|
|
30
|
+
*
|
|
31
|
+
* Returns the {@link BlobRange} to pass to `retrieveBlob` / `BlobStore.get`,
|
|
32
|
+
* `'unsatisfiable'` when the request names bytes the object does not have — answer
|
|
33
|
+
* `416`, whose `content-range` is the literal `bytes` `*` then a slash then the
|
|
34
|
+
* size — or `undefined` when there is no range to honour and the whole object
|
|
35
|
+
* should be served: an absent header, an invalid byte-range-spec (`bytes=100-50`,
|
|
36
|
+
* which RFC 9110 says to ignore rather than reject), and the forms this does not
|
|
37
|
+
* implement (multiple ranges, units other than `bytes`), which a server is
|
|
38
|
+
* always free to answer in full.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const range = parseRangeHeader(request.headers.get('range'), record.size)
|
|
43
|
+
* if (range === 'unsatisfiable') return new Response(null, { status: 416 })
|
|
44
|
+
* const blob = await retrieveBlob(
|
|
45
|
+
* persistence,
|
|
46
|
+
* record,
|
|
47
|
+
* range ? { range } : undefined,
|
|
48
|
+
* )
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseRangeHeader(header: string | null | undefined, size: number): BlobRange | 'unsatisfiable' | undefined;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
//#region src/blob-range.ts
|
|
2
|
+
/**
|
|
3
|
+
* Resolve a requested {@link BlobRange} against an object's real size, the way
|
|
4
|
+
* every byte-storing blob store has to before it slices.
|
|
5
|
+
*
|
|
6
|
+
* Clamps `length` to the end of the object and treats an absent `length` as
|
|
7
|
+
* "to the end", so the result is always the slice actually served — which is
|
|
8
|
+
* what `BlobObject.range` reports and what a `206` response's `Content-Range`
|
|
9
|
+
* is built from.
|
|
10
|
+
*
|
|
11
|
+
* Throws on an `offset` outside the object: that is a caller error, not a
|
|
12
|
+
* store error. A serve route knows the size (it is on the artifact record) and
|
|
13
|
+
* answers `416` from it, so a store only ever sees a satisfiable range unless
|
|
14
|
+
* something upstream is wrong — and silently returning an empty body there
|
|
15
|
+
* would serve a `206` that claims bytes it does not carry.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const { offset, length } = resolveBlobRange(bytes.byteLength, range)
|
|
20
|
+
* const slice = bytes.subarray(offset, offset + length)
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
function resolveBlobRange(size, range) {
|
|
24
|
+
const { offset } = range;
|
|
25
|
+
if (!Number.isInteger(offset) || offset < 0 || offset >= size) throw new RangeError(`Blob range offset ${offset} is outside the object (size ${size}).`);
|
|
26
|
+
const remaining = size - offset;
|
|
27
|
+
if (range.length === void 0) return {
|
|
28
|
+
offset,
|
|
29
|
+
length: remaining
|
|
30
|
+
};
|
|
31
|
+
if (!Number.isInteger(range.length) || range.length < 0) throw new RangeError(`Blob range length ${range.length} is not valid.`);
|
|
32
|
+
return {
|
|
33
|
+
offset,
|
|
34
|
+
length: Math.min(range.length, remaining)
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Resolve an HTTP `Range` header against a known object size, for a route that
|
|
39
|
+
* serves artifact bytes.
|
|
40
|
+
*
|
|
41
|
+
* Returns the {@link BlobRange} to pass to `retrieveBlob` / `BlobStore.get`,
|
|
42
|
+
* `'unsatisfiable'` when the request names bytes the object does not have — answer
|
|
43
|
+
* `416`, whose `content-range` is the literal `bytes` `*` then a slash then the
|
|
44
|
+
* size — or `undefined` when there is no range to honour and the whole object
|
|
45
|
+
* should be served: an absent header, an invalid byte-range-spec (`bytes=100-50`,
|
|
46
|
+
* which RFC 9110 says to ignore rather than reject), and the forms this does not
|
|
47
|
+
* implement (multiple ranges, units other than `bytes`), which a server is
|
|
48
|
+
* always free to answer in full.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const range = parseRangeHeader(request.headers.get('range'), record.size)
|
|
53
|
+
* if (range === 'unsatisfiable') return new Response(null, { status: 416 })
|
|
54
|
+
* const blob = await retrieveBlob(
|
|
55
|
+
* persistence,
|
|
56
|
+
* record,
|
|
57
|
+
* range ? { range } : undefined,
|
|
58
|
+
* )
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
function parseRangeHeader(header, size) {
|
|
62
|
+
const match = /^bytes=(\d*)-(\d*)$/.exec(header?.trim() ?? "");
|
|
63
|
+
if (!match) return void 0;
|
|
64
|
+
const [, rawStart, rawEnd] = match;
|
|
65
|
+
if (rawStart === "" && rawEnd === "") return void 0;
|
|
66
|
+
if (rawStart === "") {
|
|
67
|
+
const suffix = Number(rawEnd);
|
|
68
|
+
if (suffix === 0 || size === 0) return "unsatisfiable";
|
|
69
|
+
return { offset: Math.max(0, size - suffix) };
|
|
70
|
+
}
|
|
71
|
+
const start = Number(rawStart);
|
|
72
|
+
const end = rawEnd === "" ? void 0 : Number(rawEnd);
|
|
73
|
+
if (end !== void 0 && end < start) return void 0;
|
|
74
|
+
if (start >= size) return "unsatisfiable";
|
|
75
|
+
if (end === void 0) return { offset: start };
|
|
76
|
+
return {
|
|
77
|
+
offset: start,
|
|
78
|
+
length: Math.min(end, size - 1) - start + 1
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
export { parseRangeHeader, resolveBlobRange };
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=blob-range.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob-range.js","names":[],"sources":["../../src/blob-range.ts"],"sourcesContent":["import type { BlobRange } from './types'\n\n/**\n * Resolve a requested {@link BlobRange} against an object's real size, the way\n * every byte-storing blob store has to before it slices.\n *\n * Clamps `length` to the end of the object and treats an absent `length` as\n * \"to the end\", so the result is always the slice actually served — which is\n * what `BlobObject.range` reports and what a `206` response's `Content-Range`\n * is built from.\n *\n * Throws on an `offset` outside the object: that is a caller error, not a\n * store error. A serve route knows the size (it is on the artifact record) and\n * answers `416` from it, so a store only ever sees a satisfiable range unless\n * something upstream is wrong — and silently returning an empty body there\n * would serve a `206` that claims bytes it does not carry.\n *\n * @example\n * ```ts\n * const { offset, length } = resolveBlobRange(bytes.byteLength, range)\n * const slice = bytes.subarray(offset, offset + length)\n * ```\n */\nexport function resolveBlobRange(\n size: number,\n range: BlobRange,\n): { offset: number; length: number } {\n const { offset } = range\n if (!Number.isInteger(offset) || offset < 0 || offset >= size) {\n throw new RangeError(\n `Blob range offset ${offset} is outside the object (size ${size}).`,\n )\n }\n const remaining = size - offset\n if (range.length === undefined) return { offset, length: remaining }\n if (!Number.isInteger(range.length) || range.length < 0) {\n throw new RangeError(`Blob range length ${range.length} is not valid.`)\n }\n return { offset, length: Math.min(range.length, remaining) }\n}\n\n/**\n * Resolve an HTTP `Range` header against a known object size, for a route that\n * serves artifact bytes.\n *\n * Returns the {@link BlobRange} to pass to `retrieveBlob` / `BlobStore.get`,\n * `'unsatisfiable'` when the request names bytes the object does not have — answer\n * `416`, whose `content-range` is the literal `bytes` `*` then a slash then the\n * size — or `undefined` when there is no range to honour and the whole object\n * should be served: an absent header, an invalid byte-range-spec (`bytes=100-50`,\n * which RFC 9110 says to ignore rather than reject), and the forms this does not\n * implement (multiple ranges, units other than `bytes`), which a server is\n * always free to answer in full.\n *\n * @example\n * ```ts\n * const range = parseRangeHeader(request.headers.get('range'), record.size)\n * if (range === 'unsatisfiable') return new Response(null, { status: 416 })\n * const blob = await retrieveBlob(\n * persistence,\n * record,\n * range ? { range } : undefined,\n * )\n * ```\n */\nexport function parseRangeHeader(\n header: string | null | undefined,\n size: number,\n): BlobRange | 'unsatisfiable' | undefined {\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(header?.trim() ?? '')\n if (!match) return undefined\n const [, rawStart, rawEnd] = match\n // `bytes=-` names nothing at all.\n if (rawStart === '' && rawEnd === '') return undefined\n\n // `bytes=-500` is the LAST 500 bytes, not \"from 0 to 500\" — the one form\n // that is easy to read backwards, and reading it backwards serves the wrong\n // bytes with a 206 that claims they are the right ones.\n if (rawStart === '') {\n const suffix = Number(rawEnd)\n // A zero-length suffix names no bytes, and NO range is satisfiable against\n // a zero-byte object — without the size check, `bytes=-1` on an empty\n // artifact resolves to `{ offset: 0 }` and throws out of the store instead\n // of answering 416.\n if (suffix === 0 || size === 0) return 'unsatisfiable'\n return { offset: Math.max(0, size - suffix) }\n }\n\n const start = Number(rawStart)\n const end = rawEnd === '' ? undefined : Number(rawEnd)\n // `bytes=100-50` is an INVALID byte-range-spec, not an unsatisfiable one\n // (RFC 9110 §14.1.1). An invalid spec is ignored and the whole\n // representation is served — answering 416 would fail a request that is\n // supposed to succeed. Checked before satisfiability so the size cannot turn\n // an ignorable spec into a 416.\n if (end !== undefined && end < start) return undefined\n if (start >= size) return 'unsatisfiable'\n if (end === undefined) return { offset: start }\n // `end` is inclusive, and past the end of the object it simply clamps.\n return { offset: start, length: Math.min(end, size - 1) - start + 1 }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,iBACd,MACA,OACoC;CACpC,MAAM,EAAE,WAAW;CACnB,IAAI,CAAC,OAAO,UAAU,MAAM,KAAK,SAAS,KAAK,UAAU,MACvD,MAAM,IAAI,WACR,qBAAqB,OAAO,+BAA+B,KAAK,GAClE;CAEF,MAAM,YAAY,OAAO;CACzB,IAAI,MAAM,WAAW,KAAA,GAAW,OAAO;EAAE;EAAQ,QAAQ;CAAU;CACnE,IAAI,CAAC,OAAO,UAAU,MAAM,MAAM,KAAK,MAAM,SAAS,GACpD,MAAM,IAAI,WAAW,qBAAqB,MAAM,OAAO,eAAe;CAExE,OAAO;EAAE;EAAQ,QAAQ,KAAK,IAAI,MAAM,QAAQ,SAAS;CAAE;AAC7D;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,iBACd,QACA,MACyC;CACzC,MAAM,QAAQ,sBAAsB,KAAK,QAAQ,KAAK,KAAK,EAAE;CAC7D,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,MAAM,GAAG,UAAU,UAAU;CAE7B,IAAI,aAAa,MAAM,WAAW,IAAI,OAAO,KAAA;CAK7C,IAAI,aAAa,IAAI;EACnB,MAAM,SAAS,OAAO,MAAM;EAK5B,IAAI,WAAW,KAAK,SAAS,GAAG,OAAO;EACvC,OAAO,EAAE,QAAQ,KAAK,IAAI,GAAG,OAAO,MAAM,EAAE;CAC9C;CAEA,MAAM,QAAQ,OAAO,QAAQ;CAC7B,MAAM,MAAM,WAAW,KAAK,KAAA,IAAY,OAAO,MAAM;CAMrD,IAAI,QAAQ,KAAA,KAAa,MAAM,OAAO,OAAO,KAAA;CAC7C,IAAI,SAAS,MAAM,OAAO;CAC1B,IAAI,QAAQ,KAAA,GAAW,OAAO,EAAE,QAAQ,MAAM;CAE9C,OAAO;EAAE,QAAQ;EAAO,QAAQ,KAAK,IAAI,KAAK,OAAO,CAAC,IAAI,QAAQ;CAAE;AACtE"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { AIPersistence, InterruptStore } from './types.js';
|
|
2
|
+
export declare const PersistenceCapability: import('@tanstack/ai').Capability<AIPersistence<import('./types.js').AIPersistenceStores>, "persistence">;
|
|
3
|
+
export declare const InterruptsCapability: import('@tanstack/ai').Capability<InterruptStore, "persistence.interrupts">;
|
|
4
|
+
export declare const getPersistence: import('@tanstack/ai').CapabilityGetter<AIPersistence<import('./types.js').AIPersistenceStores>>, providePersistence: import('@tanstack/ai').CapabilityProvider<AIPersistence<import('./types.js').AIPersistenceStores>>;
|
|
5
|
+
export declare const getInterrupts: import('@tanstack/ai').CapabilityGetter<InterruptStore>, provideInterrupts: import('@tanstack/ai').CapabilityProvider<InterruptStore>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createCapability } from "@tanstack/ai";
|
|
2
|
+
//#region src/capabilities.ts
|
|
3
|
+
/**
|
|
4
|
+
* Persistence capability tokens.
|
|
5
|
+
*
|
|
6
|
+
* `withPersistence` PROVIDES persistence/interrupts so later middleware can
|
|
7
|
+
* read durable chat state. Locks live in `@tanstack/ai/locks` (`withLocks`).
|
|
8
|
+
*/
|
|
9
|
+
var PersistenceCapability = createCapability()("persistence");
|
|
10
|
+
var InterruptsCapability = createCapability()("persistence.interrupts");
|
|
11
|
+
var [getPersistence, providePersistence] = PersistenceCapability;
|
|
12
|
+
var [getInterrupts, provideInterrupts] = InterruptsCapability;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { InterruptsCapability, PersistenceCapability, getInterrupts, getPersistence, provideInterrupts, providePersistence };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","names":[],"sources":["../../src/capabilities.ts"],"sourcesContent":["/**\n * Persistence capability tokens.\n *\n * `withPersistence` PROVIDES persistence/interrupts so later middleware can\n * read durable chat state. Locks live in `@tanstack/ai/locks` (`withLocks`).\n */\nimport { createCapability } from '@tanstack/ai'\nimport type { AIPersistence, InterruptStore } from './types'\n\nexport const PersistenceCapability =\n createCapability<AIPersistence>()('persistence')\n\nexport const InterruptsCapability = createCapability<InterruptStore>()(\n 'persistence.interrupts',\n)\n\nexport const [getPersistence, providePersistence] = PersistenceCapability\nexport const [getInterrupts, provideInterrupts] = InterruptsCapability\n"],"mappings":";;;;;;;;AASA,IAAa,wBACX,iBAAgC,CAAC,CAAC,aAAa;AAEjD,IAAa,uBAAuB,iBAAiC,CAAC,CACpE,wBACF;AAEA,IAAa,CAAC,gBAAgB,sBAAsB;AACpD,IAAa,CAAC,eAAe,qBAAqB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { composePersistence, defineAIPersistence, defineMessageStore, defineRunStore, defineInterruptStore, defineMetadataStore, defineGenerationRunStore, defineArtifactStore, defineBlobStore, isTerminalRunStatus, } from './types.js';
|
|
2
|
+
export type { MessageStore, RunStatus, TerminalRunStatus, RunRecord, RunStore, InterruptRecord, InterruptStatus, InterruptStore, MetadataStore, ChatTranscriptStores, ChatPersistenceStores, ChatWithInterruptsStores, ChatTranscriptPersistence, ChatPersistence, ChatWithInterruptsPersistence, GenerationRunStatus, GenerationRunRecord, GenerationRunStore, ArtifactRecord, ArtifactStore, BlobBody, BlobRecord, BlobObject, BlobListPage, BlobPutOptions, BlobGetOptions, BlobRange, BlobListOptions, BlobStore, AIPersistence, AIPersistenceOverrides, ComposedAIPersistenceStores, Scope, } from './types.js';
|
|
3
|
+
export type { PersistedArtifactActivity, PersistedArtifactRef, PersistedArtifactRole, } from '@tanstack/ai';
|
|
4
|
+
export { withPersistence, withGenerationPersistence } from './middleware.js';
|
|
5
|
+
export type { WithPersistenceOptions, WithGenerationPersistenceOptions, ArtifactPersistenceOptions, GenerationArtifactDescriptor, GenerationArtifactExtractionInput, GenerationArtifactNameInput, } from './middleware.js';
|
|
6
|
+
export { reconstructChat } from './reconstruct.js';
|
|
7
|
+
export type { ReconstructChatOptions } from './reconstruct.js';
|
|
8
|
+
export { reconstructGeneration, getGenerationHydration, } from './reconstruct-generation.js';
|
|
9
|
+
export type { ReconstructedGeneration, ReconstructGenerationOptions, GetGenerationHydrationOptions, } from './reconstruct-generation.js';
|
|
10
|
+
export { retrieveArtifact, retrieveBlob, resolveArtifactBlobKey, } from './retrieve.js';
|
|
11
|
+
export { parseRangeHeader, resolveBlobRange } from './blob-range.js';
|
|
12
|
+
export { memoryPersistence } from './memory.js';
|
|
13
|
+
export { PersistenceCapability, InterruptsCapability, getPersistence, providePersistence, getInterrupts, provideInterrupts, } from './capabilities.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { composePersistence, defineAIPersistence, defineArtifactStore, defineBlobStore, defineGenerationRunStore, defineInterruptStore, defineMessageStore, defineMetadataStore, defineRunStore, isTerminalRunStatus } from "./types.js";
|
|
2
|
+
import { InterruptsCapability, PersistenceCapability, getInterrupts, getPersistence, provideInterrupts, providePersistence } from "./capabilities.js";
|
|
3
|
+
import { resolveArtifactBlobKey, retrieveArtifact, retrieveBlob } from "./retrieve.js";
|
|
4
|
+
import { withGenerationPersistence, withPersistence } from "./middleware.js";
|
|
5
|
+
import { reconstructChat } from "./reconstruct.js";
|
|
6
|
+
import { getGenerationHydration, reconstructGeneration } from "./reconstruct-generation.js";
|
|
7
|
+
import { parseRangeHeader, resolveBlobRange } from "./blob-range.js";
|
|
8
|
+
import { memoryPersistence } from "./memory.js";
|
|
9
|
+
export { InterruptsCapability, PersistenceCapability, composePersistence, defineAIPersistence, defineArtifactStore, defineBlobStore, defineGenerationRunStore, defineInterruptStore, defineMessageStore, defineMetadataStore, defineRunStore, getGenerationHydration, getInterrupts, getPersistence, isTerminalRunStatus, memoryPersistence, parseRangeHeader, provideInterrupts, providePersistence, reconstructChat, reconstructGeneration, resolveArtifactBlobKey, resolveBlobRange, retrieveArtifact, retrieveBlob, withGenerationPersistence, withPersistence };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ArtifactStore, BlobStore, GenerationRunStore, InterruptStore, MessageStore, MetadataStore, RunStore } from './types.js';
|
|
2
|
+
interface MemoryPersistenceStores {
|
|
3
|
+
messages: MessageStore;
|
|
4
|
+
runs: RunStore;
|
|
5
|
+
generationRuns: GenerationRunStore;
|
|
6
|
+
interrupts: InterruptStore;
|
|
7
|
+
metadata: MetadataStore;
|
|
8
|
+
artifacts: ArtifactStore;
|
|
9
|
+
blobs: BlobStore;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* In-process reference backend for the full state + generation store set.
|
|
13
|
+
*
|
|
14
|
+
* Returns messages + runs + generationRuns + interrupts + metadata + artifacts
|
|
15
|
+
* + blobs. Locks are not included — use `InMemoryLockStore` + `withLocks` from
|
|
16
|
+
* `@tanstack/ai` when a test or single-process app needs coordination.
|
|
17
|
+
*/
|
|
18
|
+
export declare function memoryPersistence(): import('./types.js').AIPersistence<MemoryPersistenceStores>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { defineAIPersistence } from "./types.js";
|
|
2
|
+
import { resolveBlobRange } from "./blob-range.js";
|
|
3
|
+
//#region src/memory.ts
|
|
4
|
+
var MemoryMessageStore = class {
|
|
5
|
+
threads = /* @__PURE__ */ new Map();
|
|
6
|
+
loadThread(threadId) {
|
|
7
|
+
return Promise.resolve(this.threads.get(threadId)?.slice() ?? []);
|
|
8
|
+
}
|
|
9
|
+
saveThread(threadId, messages) {
|
|
10
|
+
this.threads.set(threadId, messages.slice());
|
|
11
|
+
return Promise.resolve();
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var MemoryRunStore = class {
|
|
15
|
+
runs = /* @__PURE__ */ new Map();
|
|
16
|
+
createOrResume(input) {
|
|
17
|
+
const existing = this.runs.get(input.runId);
|
|
18
|
+
if (existing) return Promise.resolve(existing);
|
|
19
|
+
const record = {
|
|
20
|
+
runId: input.runId,
|
|
21
|
+
threadId: input.threadId,
|
|
22
|
+
status: input.status ?? "running",
|
|
23
|
+
startedAt: input.startedAt
|
|
24
|
+
};
|
|
25
|
+
this.runs.set(record.runId, record);
|
|
26
|
+
return Promise.resolve(record);
|
|
27
|
+
}
|
|
28
|
+
update(runId, patch) {
|
|
29
|
+
const existing = this.runs.get(runId);
|
|
30
|
+
if (existing) this.runs.set(runId, {
|
|
31
|
+
...existing,
|
|
32
|
+
...patch
|
|
33
|
+
});
|
|
34
|
+
return Promise.resolve();
|
|
35
|
+
}
|
|
36
|
+
get(runId) {
|
|
37
|
+
return Promise.resolve(this.runs.get(runId) ?? null);
|
|
38
|
+
}
|
|
39
|
+
findActiveRun(threadId) {
|
|
40
|
+
const active = [...this.runs.values()].filter((run) => run.threadId === threadId && run.status === "running").sort((a, b) => b.startedAt - a.startedAt);
|
|
41
|
+
return Promise.resolve(active[0] ?? null);
|
|
42
|
+
}
|
|
43
|
+
listByThread(threadId) {
|
|
44
|
+
const matching = [...this.runs.values()].filter((run) => run.threadId === threadId).sort((a, b) => a.startedAt - b.startedAt);
|
|
45
|
+
return Promise.resolve(matching);
|
|
46
|
+
}
|
|
47
|
+
listReclaimable(opts) {
|
|
48
|
+
const cutoff = opts.now - opts.ttlMs;
|
|
49
|
+
const matching = [...this.runs.values()].filter((run) => run.status === "running" && run.detachedSince !== void 0 && run.detachedSince <= cutoff);
|
|
50
|
+
return Promise.resolve(matching);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var MemoryGenerationRunStore = class {
|
|
54
|
+
generationRuns = /* @__PURE__ */ new Map();
|
|
55
|
+
createOrResume(input) {
|
|
56
|
+
const existing = this.generationRuns.get(input.runId);
|
|
57
|
+
if (existing) return Promise.resolve(existing);
|
|
58
|
+
const record = {
|
|
59
|
+
runId: input.runId,
|
|
60
|
+
threadId: input.threadId,
|
|
61
|
+
activity: input.activity,
|
|
62
|
+
provider: input.provider,
|
|
63
|
+
model: input.model,
|
|
64
|
+
status: input.status ?? "running",
|
|
65
|
+
startedAt: input.startedAt
|
|
66
|
+
};
|
|
67
|
+
this.generationRuns.set(record.runId, record);
|
|
68
|
+
return Promise.resolve(record);
|
|
69
|
+
}
|
|
70
|
+
update(runId, patch) {
|
|
71
|
+
const existing = this.generationRuns.get(runId);
|
|
72
|
+
if (existing) this.generationRuns.set(runId, {
|
|
73
|
+
...existing,
|
|
74
|
+
...patch
|
|
75
|
+
});
|
|
76
|
+
return Promise.resolve();
|
|
77
|
+
}
|
|
78
|
+
get(runId) {
|
|
79
|
+
return Promise.resolve(this.generationRuns.get(runId) ?? null);
|
|
80
|
+
}
|
|
81
|
+
findLatestForThread(threadId) {
|
|
82
|
+
const linked = [...this.generationRuns.values()].filter((run) => run.threadId === threadId).sort((a, b) => b.startedAt - a.startedAt);
|
|
83
|
+
return Promise.resolve(linked[0] ?? null);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
function byRequestedAt(a, b) {
|
|
87
|
+
return a.requestedAt - b.requestedAt;
|
|
88
|
+
}
|
|
89
|
+
var MemoryInterruptStore = class {
|
|
90
|
+
interrupts = /* @__PURE__ */ new Map();
|
|
91
|
+
create(record) {
|
|
92
|
+
if (!this.interrupts.has(record.interruptId)) this.interrupts.set(record.interruptId, {
|
|
93
|
+
...record,
|
|
94
|
+
status: "pending"
|
|
95
|
+
});
|
|
96
|
+
return Promise.resolve();
|
|
97
|
+
}
|
|
98
|
+
resolve(interruptId, response) {
|
|
99
|
+
const existing = this.interrupts.get(interruptId);
|
|
100
|
+
if (existing) this.interrupts.set(interruptId, {
|
|
101
|
+
...existing,
|
|
102
|
+
status: "resolved",
|
|
103
|
+
resolvedAt: Date.now(),
|
|
104
|
+
response
|
|
105
|
+
});
|
|
106
|
+
return Promise.resolve();
|
|
107
|
+
}
|
|
108
|
+
cancel(interruptId) {
|
|
109
|
+
const existing = this.interrupts.get(interruptId);
|
|
110
|
+
if (existing) this.interrupts.set(interruptId, {
|
|
111
|
+
...existing,
|
|
112
|
+
status: "cancelled",
|
|
113
|
+
resolvedAt: Date.now()
|
|
114
|
+
});
|
|
115
|
+
return Promise.resolve();
|
|
116
|
+
}
|
|
117
|
+
get(interruptId) {
|
|
118
|
+
return Promise.resolve(this.interrupts.get(interruptId) ?? null);
|
|
119
|
+
}
|
|
120
|
+
list(threadId) {
|
|
121
|
+
return Promise.resolve([...this.interrupts.values()].filter((interrupt) => interrupt.threadId === threadId).sort(byRequestedAt));
|
|
122
|
+
}
|
|
123
|
+
listPending(threadId) {
|
|
124
|
+
return Promise.resolve([...this.interrupts.values()].filter((interrupt) => interrupt.threadId === threadId && interrupt.status === "pending").sort(byRequestedAt));
|
|
125
|
+
}
|
|
126
|
+
listByRun(runId) {
|
|
127
|
+
return Promise.resolve([...this.interrupts.values()].filter((interrupt) => interrupt.runId === runId).sort(byRequestedAt));
|
|
128
|
+
}
|
|
129
|
+
listPendingByRun(runId) {
|
|
130
|
+
return Promise.resolve([...this.interrupts.values()].filter((interrupt) => interrupt.runId === runId && interrupt.status === "pending").sort(byRequestedAt));
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
var MemoryMetadataStore = class {
|
|
134
|
+
values = /* @__PURE__ */ new Map();
|
|
135
|
+
get(namespace, key) {
|
|
136
|
+
const bucket = this.values.get(namespace);
|
|
137
|
+
if (!bucket || !bucket.has(key)) return Promise.resolve(null);
|
|
138
|
+
return Promise.resolve(bucket.get(key));
|
|
139
|
+
}
|
|
140
|
+
set(namespace, key, value) {
|
|
141
|
+
let bucket = this.values.get(namespace);
|
|
142
|
+
if (!bucket) {
|
|
143
|
+
bucket = /* @__PURE__ */ new Map();
|
|
144
|
+
this.values.set(namespace, bucket);
|
|
145
|
+
}
|
|
146
|
+
bucket.set(key, value);
|
|
147
|
+
return Promise.resolve();
|
|
148
|
+
}
|
|
149
|
+
delete(namespace, key) {
|
|
150
|
+
const bucket = this.values.get(namespace);
|
|
151
|
+
if (!bucket) return Promise.resolve();
|
|
152
|
+
bucket.delete(key);
|
|
153
|
+
if (bucket.size === 0) this.values.delete(namespace);
|
|
154
|
+
return Promise.resolve();
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
var MemoryArtifactStore = class {
|
|
158
|
+
artifacts = /* @__PURE__ */ new Map();
|
|
159
|
+
save(record) {
|
|
160
|
+
this.artifacts.set(record.artifactId, { ...record });
|
|
161
|
+
return Promise.resolve();
|
|
162
|
+
}
|
|
163
|
+
get(artifactId) {
|
|
164
|
+
return Promise.resolve(this.artifacts.get(artifactId) ?? null);
|
|
165
|
+
}
|
|
166
|
+
list(runId) {
|
|
167
|
+
return Promise.resolve([...this.artifacts.values()].filter((a) => a.runId === runId));
|
|
168
|
+
}
|
|
169
|
+
delete(artifactId) {
|
|
170
|
+
this.artifacts.delete(artifactId);
|
|
171
|
+
return Promise.resolve();
|
|
172
|
+
}
|
|
173
|
+
deleteForRun(runId) {
|
|
174
|
+
for (const artifact of this.artifacts.values()) if (artifact.runId === runId) this.artifacts.delete(artifact.artifactId);
|
|
175
|
+
return Promise.resolve();
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
var textEncoder = new TextEncoder();
|
|
179
|
+
var textDecoder = new TextDecoder();
|
|
180
|
+
function copyBytes(bytes) {
|
|
181
|
+
return new Uint8Array(bytes);
|
|
182
|
+
}
|
|
183
|
+
function bytesToArrayBuffer(bytes) {
|
|
184
|
+
const buffer = new ArrayBuffer(bytes.byteLength);
|
|
185
|
+
new Uint8Array(buffer).set(bytes);
|
|
186
|
+
return buffer;
|
|
187
|
+
}
|
|
188
|
+
async function bytesFromStream(stream) {
|
|
189
|
+
const reader = stream.getReader();
|
|
190
|
+
const chunks = [];
|
|
191
|
+
let total = 0;
|
|
192
|
+
try {
|
|
193
|
+
while (true) {
|
|
194
|
+
const { done, value } = await reader.read();
|
|
195
|
+
if (done) break;
|
|
196
|
+
chunks.push(copyBytes(value));
|
|
197
|
+
total += value.byteLength;
|
|
198
|
+
}
|
|
199
|
+
} finally {
|
|
200
|
+
reader.releaseLock();
|
|
201
|
+
}
|
|
202
|
+
const bytes = new Uint8Array(total);
|
|
203
|
+
let offset = 0;
|
|
204
|
+
for (const chunk of chunks) {
|
|
205
|
+
bytes.set(chunk, offset);
|
|
206
|
+
offset += chunk.byteLength;
|
|
207
|
+
}
|
|
208
|
+
return bytes;
|
|
209
|
+
}
|
|
210
|
+
async function bytesFromBlobBody(body) {
|
|
211
|
+
if (typeof body === "string") return textEncoder.encode(body);
|
|
212
|
+
if (body instanceof ArrayBuffer) return new Uint8Array(body.slice(0));
|
|
213
|
+
if (ArrayBuffer.isView(body)) return copyBytes(new Uint8Array(body.buffer, body.byteOffset, body.byteLength));
|
|
214
|
+
if (typeof Blob !== "undefined" && body instanceof Blob) return new Uint8Array(await body.arrayBuffer());
|
|
215
|
+
if (typeof ReadableStream !== "undefined" && body instanceof ReadableStream) return bytesFromStream(body);
|
|
216
|
+
throw new TypeError("Unsupported blob body.");
|
|
217
|
+
}
|
|
218
|
+
function blobRecordSnapshot(record) {
|
|
219
|
+
return {
|
|
220
|
+
...record,
|
|
221
|
+
...record.customMetadata ? { customMetadata: { ...record.customMetadata } } : {}
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function blobObject(record, bytes, range) {
|
|
225
|
+
const served = range ? resolveBlobRange(bytes.byteLength, range) : {
|
|
226
|
+
offset: 0,
|
|
227
|
+
length: bytes.byteLength
|
|
228
|
+
};
|
|
229
|
+
const copied = copyBytes(bytes.subarray(served.offset, served.offset + served.length));
|
|
230
|
+
return {
|
|
231
|
+
...blobRecordSnapshot(record),
|
|
232
|
+
...range ? { range: served } : {},
|
|
233
|
+
body: new ReadableStream({ start(controller) {
|
|
234
|
+
controller.enqueue(copyBytes(copied));
|
|
235
|
+
controller.close();
|
|
236
|
+
} }),
|
|
237
|
+
arrayBuffer: () => Promise.resolve(bytesToArrayBuffer(copied)),
|
|
238
|
+
text: () => Promise.resolve(textDecoder.decode(copied))
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
var MemoryBlobStore = class {
|
|
242
|
+
blobs = /* @__PURE__ */ new Map();
|
|
243
|
+
nextEtag = 1;
|
|
244
|
+
async put(key, body, options) {
|
|
245
|
+
const bytes = await bytesFromBlobBody(body);
|
|
246
|
+
const existing = this.blobs.get(key);
|
|
247
|
+
const now = Date.now();
|
|
248
|
+
const record = {
|
|
249
|
+
key,
|
|
250
|
+
size: bytes.byteLength,
|
|
251
|
+
etag: String(this.nextEtag++),
|
|
252
|
+
contentType: options?.contentType ?? (typeof Blob !== "undefined" && body instanceof Blob ? body.type || void 0 : void 0),
|
|
253
|
+
customMetadata: options?.customMetadata ? { ...options.customMetadata } : void 0,
|
|
254
|
+
createdAt: existing?.record.createdAt ?? now,
|
|
255
|
+
updatedAt: now
|
|
256
|
+
};
|
|
257
|
+
this.blobs.set(key, {
|
|
258
|
+
record,
|
|
259
|
+
bytes: copyBytes(bytes)
|
|
260
|
+
});
|
|
261
|
+
return blobRecordSnapshot(record);
|
|
262
|
+
}
|
|
263
|
+
get(key, options) {
|
|
264
|
+
const entry = this.blobs.get(key);
|
|
265
|
+
return Promise.resolve(entry ? blobObject(entry.record, entry.bytes, options?.range) : null);
|
|
266
|
+
}
|
|
267
|
+
head(key) {
|
|
268
|
+
const entry = this.blobs.get(key);
|
|
269
|
+
return Promise.resolve(entry ? blobRecordSnapshot(entry.record) : null);
|
|
270
|
+
}
|
|
271
|
+
delete(key) {
|
|
272
|
+
this.blobs.delete(key);
|
|
273
|
+
return Promise.resolve();
|
|
274
|
+
}
|
|
275
|
+
list(options) {
|
|
276
|
+
const limit = options?.limit;
|
|
277
|
+
if (limit === 0) return Promise.resolve({
|
|
278
|
+
objects: [],
|
|
279
|
+
truncated: false
|
|
280
|
+
});
|
|
281
|
+
const keys = [...this.blobs.keys()].filter((key) => key.startsWith(options?.prefix ?? "")).filter((key) => options?.cursor === void 0 || key > options.cursor).sort();
|
|
282
|
+
const pageKeys = limit === void 0 ? keys : keys.slice(0, limit);
|
|
283
|
+
const objects = pageKeys.map((key) => {
|
|
284
|
+
const blob = this.blobs.get(key);
|
|
285
|
+
if (blob === void 0) throw new Error(`Missing blob for listed key: ${key}`);
|
|
286
|
+
return blobRecordSnapshot(blob.record);
|
|
287
|
+
});
|
|
288
|
+
const truncated = limit !== void 0 && keys.length > limit;
|
|
289
|
+
return Promise.resolve({
|
|
290
|
+
objects,
|
|
291
|
+
...truncated ? {
|
|
292
|
+
cursor: pageKeys.at(-1),
|
|
293
|
+
truncated
|
|
294
|
+
} : {}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* In-process reference backend for the full state + generation store set.
|
|
300
|
+
*
|
|
301
|
+
* Returns messages + runs + generationRuns + interrupts + metadata + artifacts
|
|
302
|
+
* + blobs. Locks are not included — use `InMemoryLockStore` + `withLocks` from
|
|
303
|
+
* `@tanstack/ai` when a test or single-process app needs coordination.
|
|
304
|
+
*/
|
|
305
|
+
function memoryPersistence() {
|
|
306
|
+
return defineAIPersistence({ stores: {
|
|
307
|
+
messages: new MemoryMessageStore(),
|
|
308
|
+
runs: new MemoryRunStore(),
|
|
309
|
+
generationRuns: new MemoryGenerationRunStore(),
|
|
310
|
+
interrupts: new MemoryInterruptStore(),
|
|
311
|
+
metadata: new MemoryMetadataStore(),
|
|
312
|
+
artifacts: new MemoryArtifactStore(),
|
|
313
|
+
blobs: new MemoryBlobStore()
|
|
314
|
+
} });
|
|
315
|
+
}
|
|
316
|
+
//#endregion
|
|
317
|
+
export { memoryPersistence };
|
|
318
|
+
|
|
319
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","names":[],"sources":["../../src/memory.ts"],"sourcesContent":["import { defineAIPersistence } from './types'\nimport { resolveBlobRange } from './blob-range'\nimport type { ModelMessage } from '@tanstack/ai'\nimport type {\n ArtifactRecord,\n ArtifactStore,\n BlobBody,\n BlobGetOptions,\n BlobListOptions,\n BlobObject,\n BlobPutOptions,\n BlobRange,\n BlobRecord,\n BlobStore,\n GenerationRunRecord,\n GenerationRunStore,\n InterruptRecord,\n InterruptStore,\n MessageStore,\n MetadataStore,\n RunRecord,\n RunStore,\n} from './types'\n\nclass MemoryMessageStore implements MessageStore {\n private readonly threads = new Map<string, Array<ModelMessage>>()\n loadThread(threadId: string): Promise<Array<ModelMessage>> {\n return Promise.resolve(this.threads.get(threadId)?.slice() ?? [])\n }\n saveThread(threadId: string, messages: Array<ModelMessage>): Promise<void> {\n this.threads.set(threadId, messages.slice())\n return Promise.resolve()\n }\n}\n\nclass MemoryRunStore implements RunStore {\n private readonly runs = new Map<string, RunRecord>()\n createOrResume(input: {\n runId: string\n threadId: string\n status?: RunRecord['status']\n startedAt: number\n }): Promise<RunRecord> {\n const existing = this.runs.get(input.runId)\n if (existing) return Promise.resolve(existing)\n const record: RunRecord = {\n runId: input.runId,\n threadId: input.threadId,\n status: input.status ?? 'running',\n startedAt: input.startedAt,\n }\n this.runs.set(record.runId, record)\n return Promise.resolve(record)\n }\n update(\n runId: string,\n patch: Partial<\n Pick<\n RunRecord,\n | 'status'\n | 'finishedAt'\n | 'error'\n | 'usage'\n | 'sandboxKey'\n | 'detachedSince'\n | 'cancelRequested'\n | 'driverEpoch'\n >\n >,\n ): Promise<void> {\n const existing = this.runs.get(runId)\n if (existing) this.runs.set(runId, { ...existing, ...patch })\n return Promise.resolve()\n }\n get(runId: string): Promise<RunRecord | null> {\n return Promise.resolve(this.runs.get(runId) ?? null)\n }\n findActiveRun(threadId: string): Promise<RunRecord | null> {\n const active = [...this.runs.values()]\n .filter((run) => run.threadId === threadId && run.status === 'running')\n .sort((a, b) => b.startedAt - a.startedAt)\n return Promise.resolve(active[0] ?? null)\n }\n listByThread(threadId: string): Promise<Array<RunRecord>> {\n const matching = [...this.runs.values()]\n .filter((run) => run.threadId === threadId)\n .sort((a, b) => a.startedAt - b.startedAt)\n return Promise.resolve(matching)\n }\n listReclaimable(opts: {\n now: number\n ttlMs: number\n }): Promise<Array<RunRecord>> {\n const cutoff = opts.now - opts.ttlMs\n const matching = [...this.runs.values()].filter(\n (run) =>\n run.status === 'running' &&\n run.detachedSince !== undefined &&\n run.detachedSince <= cutoff,\n )\n return Promise.resolve(matching)\n }\n}\n\nclass MemoryGenerationRunStore implements GenerationRunStore {\n private readonly generationRuns = new Map<string, GenerationRunRecord>()\n createOrResume(\n input: Pick<\n GenerationRunRecord,\n 'runId' | 'threadId' | 'activity' | 'provider' | 'model' | 'startedAt'\n > & { status?: GenerationRunRecord['status'] },\n ): Promise<GenerationRunRecord> {\n const existing = this.generationRuns.get(input.runId)\n if (existing) return Promise.resolve(existing)\n const record: GenerationRunRecord = {\n runId: input.runId,\n threadId: input.threadId,\n activity: input.activity,\n provider: input.provider,\n model: input.model,\n status: input.status ?? 'running',\n startedAt: input.startedAt,\n }\n this.generationRuns.set(record.runId, record)\n return Promise.resolve(record)\n }\n update(\n runId: string,\n patch: Partial<\n Pick<\n GenerationRunRecord,\n 'status' | 'finishedAt' | 'error' | 'result' | 'artifacts' | 'usage'\n >\n >,\n ): Promise<void> {\n const existing = this.generationRuns.get(runId)\n if (existing) this.generationRuns.set(runId, { ...existing, ...patch })\n return Promise.resolve()\n }\n get(runId: string): Promise<GenerationRunRecord | null> {\n return Promise.resolve(this.generationRuns.get(runId) ?? null)\n }\n findLatestForThread(threadId: string): Promise<GenerationRunRecord | null> {\n const linked = [...this.generationRuns.values()]\n .filter((run) => run.threadId === threadId)\n .sort((a, b) => b.startedAt - a.startedAt)\n return Promise.resolve(linked[0] ?? null)\n }\n}\n\nfunction byRequestedAt(a: InterruptRecord, b: InterruptRecord): number {\n return a.requestedAt - b.requestedAt\n}\n\nclass MemoryInterruptStore implements InterruptStore {\n private readonly interrupts = new Map<string, InterruptRecord>()\n create(\n record: Omit<InterruptRecord, 'status' | 'resolvedAt'>,\n ): Promise<void> {\n // Insert-if-absent (canonical semantics, matching the SQL backends'\n // ON CONFLICT DO NOTHING): a duplicate id must never clobber an existing —\n // possibly already resolved — interrupt back to pending.\n if (!this.interrupts.has(record.interruptId)) {\n this.interrupts.set(record.interruptId, { ...record, status: 'pending' })\n }\n return Promise.resolve()\n }\n resolve(interruptId: string, response?: unknown): Promise<void> {\n const existing = this.interrupts.get(interruptId)\n if (existing) {\n this.interrupts.set(interruptId, {\n ...existing,\n status: 'resolved',\n resolvedAt: Date.now(),\n response,\n })\n }\n return Promise.resolve()\n }\n cancel(interruptId: string): Promise<void> {\n const existing = this.interrupts.get(interruptId)\n if (existing) {\n this.interrupts.set(interruptId, {\n ...existing,\n status: 'cancelled',\n resolvedAt: Date.now(),\n })\n }\n return Promise.resolve()\n }\n get(interruptId: string): Promise<InterruptRecord | null> {\n return Promise.resolve(this.interrupts.get(interruptId) ?? null)\n }\n list(threadId: string): Promise<Array<InterruptRecord>> {\n return Promise.resolve(\n [...this.interrupts.values()]\n .filter((interrupt) => interrupt.threadId === threadId)\n .sort(byRequestedAt),\n )\n }\n listPending(threadId: string): Promise<Array<InterruptRecord>> {\n return Promise.resolve(\n [...this.interrupts.values()]\n .filter(\n (interrupt) =>\n interrupt.threadId === threadId && interrupt.status === 'pending',\n )\n .sort(byRequestedAt),\n )\n }\n listByRun(runId: string): Promise<Array<InterruptRecord>> {\n return Promise.resolve(\n [...this.interrupts.values()]\n .filter((interrupt) => interrupt.runId === runId)\n .sort(byRequestedAt),\n )\n }\n listPendingByRun(runId: string): Promise<Array<InterruptRecord>> {\n return Promise.resolve(\n [...this.interrupts.values()]\n .filter(\n (interrupt) =>\n interrupt.runId === runId && interrupt.status === 'pending',\n )\n .sort(byRequestedAt),\n )\n }\n}\n\nclass MemoryMetadataStore implements MetadataStore {\n // Nested maps so composite identity is `(namespace, key)` without the\n // `${namespace}:${key}` collision where `('a:b','c')` aliases `('a','b:c')`.\n // (This parameter is an app-defined metadata namespace string — not the\n // shared `Scope` identity type from `@tanstack/ai`.)\n private readonly values = new Map<string, Map<string, unknown>>()\n get(namespace: string, key: string): Promise<unknown | null> {\n const bucket = this.values.get(namespace)\n if (!bucket || !bucket.has(key)) return Promise.resolve(null)\n return Promise.resolve(bucket.get(key))\n }\n set(namespace: string, key: string, value: unknown): Promise<void> {\n let bucket = this.values.get(namespace)\n if (!bucket) {\n bucket = new Map()\n this.values.set(namespace, bucket)\n }\n bucket.set(key, value)\n return Promise.resolve()\n }\n delete(namespace: string, key: string): Promise<void> {\n const bucket = this.values.get(namespace)\n if (!bucket) return Promise.resolve()\n bucket.delete(key)\n if (bucket.size === 0) this.values.delete(namespace)\n return Promise.resolve()\n }\n}\n\nclass MemoryArtifactStore implements ArtifactStore {\n private readonly artifacts = new Map<string, ArtifactRecord>()\n save(record: ArtifactRecord): Promise<void> {\n this.artifacts.set(record.artifactId, { ...record })\n return Promise.resolve()\n }\n get(artifactId: string): Promise<ArtifactRecord | null> {\n return Promise.resolve(this.artifacts.get(artifactId) ?? null)\n }\n list(runId: string): Promise<Array<ArtifactRecord>> {\n return Promise.resolve(\n [...this.artifacts.values()].filter((a) => a.runId === runId),\n )\n }\n delete(artifactId: string): Promise<void> {\n this.artifacts.delete(artifactId)\n return Promise.resolve()\n }\n deleteForRun(runId: string): Promise<void> {\n for (const artifact of this.artifacts.values()) {\n if (artifact.runId === runId) this.artifacts.delete(artifact.artifactId)\n }\n return Promise.resolve()\n }\n}\n\ninterface MemoryBlobEntry {\n record: BlobRecord\n bytes: Uint8Array\n}\n\nconst textEncoder = new TextEncoder()\nconst textDecoder = new TextDecoder()\n\nfunction copyBytes(bytes: Uint8Array): Uint8Array {\n return new Uint8Array(bytes)\n}\n\nfunction bytesToArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n const buffer = new ArrayBuffer(bytes.byteLength)\n new Uint8Array(buffer).set(bytes)\n return buffer\n}\n\nasync function bytesFromStream(\n stream: ReadableStream<Uint8Array>,\n): Promise<Uint8Array> {\n const reader = stream.getReader()\n const chunks: Array<Uint8Array> = []\n let total = 0\n try {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n chunks.push(copyBytes(value))\n total += value.byteLength\n }\n } finally {\n reader.releaseLock()\n }\n\n const bytes = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n return bytes\n}\n\nasync function bytesFromBlobBody(body: BlobBody): Promise<Uint8Array> {\n if (typeof body === 'string') {\n return textEncoder.encode(body)\n }\n if (body instanceof ArrayBuffer) {\n return new Uint8Array(body.slice(0))\n }\n if (ArrayBuffer.isView(body)) {\n return copyBytes(\n new Uint8Array(body.buffer, body.byteOffset, body.byteLength),\n )\n }\n if (typeof Blob !== 'undefined' && body instanceof Blob) {\n return new Uint8Array(await body.arrayBuffer())\n }\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n return bytesFromStream(body)\n }\n throw new TypeError('Unsupported blob body.')\n}\n\nfunction blobRecordSnapshot(record: BlobRecord): BlobRecord {\n return {\n ...record,\n ...(record.customMetadata\n ? { customMetadata: { ...record.customMetadata } }\n : {}),\n }\n}\n\nfunction blobObject(\n record: BlobRecord,\n bytes: Uint8Array,\n range?: BlobRange,\n): BlobObject {\n // `size` keeps reporting the whole object; only the bytes narrow.\n const served = range\n ? resolveBlobRange(bytes.byteLength, range)\n : { offset: 0, length: bytes.byteLength }\n const copied = copyBytes(\n bytes.subarray(served.offset, served.offset + served.length),\n )\n return {\n ...blobRecordSnapshot(record),\n ...(range ? { range: served } : {}),\n body: new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(copyBytes(copied))\n controller.close()\n },\n }),\n arrayBuffer: () => Promise.resolve(bytesToArrayBuffer(copied)),\n text: () => Promise.resolve(textDecoder.decode(copied)),\n }\n}\n\nclass MemoryBlobStore implements BlobStore {\n private readonly blobs = new Map<string, MemoryBlobEntry>()\n private nextEtag = 1\n\n async put(\n key: string,\n body: BlobBody,\n options?: BlobPutOptions,\n ): Promise<BlobRecord> {\n const bytes = await bytesFromBlobBody(body)\n const existing = this.blobs.get(key)\n const now = Date.now()\n const record: BlobRecord = {\n key,\n size: bytes.byteLength,\n etag: String(this.nextEtag++),\n contentType:\n options?.contentType ??\n (typeof Blob !== 'undefined' && body instanceof Blob\n ? body.type || undefined\n : undefined),\n customMetadata: options?.customMetadata\n ? { ...options.customMetadata }\n : undefined,\n createdAt: existing?.record.createdAt ?? now,\n updatedAt: now,\n }\n this.blobs.set(key, { record, bytes: copyBytes(bytes) })\n return blobRecordSnapshot(record)\n }\n\n get(key: string, options?: BlobGetOptions): Promise<BlobObject | null> {\n const entry = this.blobs.get(key)\n return Promise.resolve(\n entry ? blobObject(entry.record, entry.bytes, options?.range) : null,\n )\n }\n\n head(key: string): Promise<BlobRecord | null> {\n const entry = this.blobs.get(key)\n return Promise.resolve(entry ? blobRecordSnapshot(entry.record) : null)\n }\n\n delete(key: string): Promise<void> {\n this.blobs.delete(key)\n return Promise.resolve()\n }\n\n list(options?: BlobListOptions): Promise<{\n objects: Array<BlobRecord>\n cursor?: string\n truncated?: boolean\n }> {\n const limit = options?.limit\n if (limit === 0) {\n return Promise.resolve({ objects: [], truncated: false })\n }\n const keys = [...this.blobs.keys()]\n .filter((key) => key.startsWith(options?.prefix ?? ''))\n .filter((key) => options?.cursor === undefined || key > options.cursor)\n .sort()\n const pageKeys = limit === undefined ? keys : keys.slice(0, limit)\n const objects = pageKeys.map((key) => {\n const blob = this.blobs.get(key)\n if (blob === undefined) {\n throw new Error(`Missing blob for listed key: ${key}`)\n }\n return blobRecordSnapshot(blob.record)\n })\n const truncated = limit !== undefined && keys.length > limit\n return Promise.resolve({\n objects,\n ...(truncated ? { cursor: pageKeys.at(-1), truncated } : {}),\n })\n }\n}\n\ninterface MemoryPersistenceStores {\n messages: MessageStore\n runs: RunStore\n generationRuns: GenerationRunStore\n interrupts: InterruptStore\n metadata: MetadataStore\n artifacts: ArtifactStore\n blobs: BlobStore\n}\n\n/**\n * In-process reference backend for the full state + generation store set.\n *\n * Returns messages + runs + generationRuns + interrupts + metadata + artifacts\n * + blobs. Locks are not included — use `InMemoryLockStore` + `withLocks` from\n * `@tanstack/ai` when a test or single-process app needs coordination.\n */\nexport function memoryPersistence() {\n const stores: MemoryPersistenceStores = {\n messages: new MemoryMessageStore(),\n runs: new MemoryRunStore(),\n generationRuns: new MemoryGenerationRunStore(),\n interrupts: new MemoryInterruptStore(),\n metadata: new MemoryMetadataStore(),\n artifacts: new MemoryArtifactStore(),\n blobs: new MemoryBlobStore(),\n }\n return defineAIPersistence({ stores })\n}\n"],"mappings":"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"}
|