@tanstack/ai-persistence 0.6.4 → 0.6.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/dist/esm/memory.js +8 -1
- package/dist/esm/memory.js.map +1 -1
- package/dist/esm/merge-stored.d.ts +2 -0
- package/dist/esm/merge-stored.js +47 -0
- package/dist/esm/merge-stored.js.map +1 -0
- package/dist/esm/middleware.d.ts +2 -1
- package/dist/esm/middleware.js +18 -44
- package/dist/esm/middleware.js.map +1 -1
- package/dist/esm/reconstruct.js +90 -1
- package/dist/esm/reconstruct.js.map +1 -1
- package/dist/esm/subagent-runs.d.ts +40 -0
- package/dist/esm/subagent-runs.js +366 -0
- package/dist/esm/subagent-runs.js.map +1 -0
- package/dist/esm/testkit/conformance.d.ts +4 -1
- package/dist/esm/testkit/conformance.js +94 -1
- package/dist/esm/testkit/conformance.js.map +1 -1
- package/package.json +3 -3
- package/skills/ai-persistence/build-cloudflare-adapter/SKILL.md +41 -16
- package/skills/ai-persistence/build-custom-adapter/SKILL.md +49 -20
- package/skills/ai-persistence/build-drizzle-adapter/SKILL.md +54 -9
- package/skills/ai-persistence/build-prisma-adapter/SKILL.md +32 -6
- package/skills/ai-persistence/stores/SKILL.md +46 -29
- package/src/memory.ts +16 -0
- package/src/merge-stored.ts +67 -0
- package/src/middleware.ts +19 -67
- package/src/reconstruct.ts +155 -2
- package/src/subagent-runs.ts +581 -0
- package/src/testkit/conformance.ts +125 -2
package/dist/esm/memory.js
CHANGED
|
@@ -31,7 +31,10 @@ var MemoryRunStore = class {
|
|
|
31
31
|
runId: input.runId,
|
|
32
32
|
threadId: input.threadId,
|
|
33
33
|
status: input.status ?? "running",
|
|
34
|
-
startedAt: input.startedAt
|
|
34
|
+
startedAt: input.startedAt,
|
|
35
|
+
...input.parentRunId !== void 0 ? { parentRunId: input.parentRunId } : {},
|
|
36
|
+
...input.subagentRunId !== void 0 ? { subagentRunId: input.subagentRunId } : {},
|
|
37
|
+
...input.name !== void 0 ? { name: input.name } : {}
|
|
35
38
|
};
|
|
36
39
|
this.runs.set(record.runId, record);
|
|
37
40
|
return Promise.resolve(record);
|
|
@@ -55,6 +58,10 @@ var MemoryRunStore = class {
|
|
|
55
58
|
const matching = [...this.runs.values()].filter((run) => run.threadId === threadId).sort((a, b) => a.startedAt - b.startedAt);
|
|
56
59
|
return Promise.resolve(matching);
|
|
57
60
|
}
|
|
61
|
+
listByParentRun(parentRunId) {
|
|
62
|
+
const matching = [...this.runs.values()].filter((run) => run.parentRunId === parentRunId).sort((a, b) => a.startedAt - b.startedAt);
|
|
63
|
+
return Promise.resolve(matching);
|
|
64
|
+
}
|
|
58
65
|
listReclaimable(opts) {
|
|
59
66
|
const cutoff = opts.now - opts.ttlMs;
|
|
60
67
|
const matching = [...this.runs.values()].filter((run) => run.status === "running" && run.detachedSince !== void 0 && run.detachedSince <= cutoff);
|
package/dist/esm/memory.js.map
CHANGED
|
@@ -1 +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 InterruptCommitEntry,\n InterruptRecord,\n InterruptStore,\n MessageStore,\n MetadataStore,\n RunRecord,\n RunStore,\n} from './types'\n\nconst compareUtf8Bytes = (left: string, right: string): number => {\n const leftBytes = new TextEncoder().encode(left)\n const rightBytes = new TextEncoder().encode(right)\n const length = Math.min(leftBytes.length, rightBytes.length)\n\n for (let index = 0; index < length; index++) {\n const leftByte = leftBytes[index]\n const rightByte = rightBytes[index]\n if (leftByte !== rightByte) {\n return (leftByte ?? 0) - (rightByte ?? 0)\n }\n }\n\n return leftBytes.length - rightBytes.length\n}\n\nclass MemoryMessageStore implements MessageStore {\n private readonly threads = new Map<string, Array<ModelMessage>>()\n loadThread(\n threadId: string,\n _options?: { limit?: number; before?: string },\n ): 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 async commitBatch(\n entries: ReadonlyArray<InterruptCommitEntry>,\n ): Promise<void> {\n const ids = new Set<string>()\n for (const entry of entries) {\n if (ids.has(entry.interruptId)) {\n throw new Error(\n `Interrupt batch contains duplicate id: ${entry.interruptId}.`,\n )\n }\n ids.add(entry.interruptId)\n const existing = this.interrupts.get(entry.interruptId)\n if (!existing) {\n throw new Error(\n `Interrupt batch references missing id: ${entry.interruptId}.`,\n )\n }\n if (existing.status !== 'pending') {\n throw new Error(\n `Interrupt batch references non-pending id: ${entry.interruptId}.`,\n )\n }\n }\n const resolvedAt = Date.now()\n for (const entry of entries) {\n const existing = this.interrupts.get(entry.interruptId)\n if (!existing) continue\n if (entry.status === 'resolved') {\n this.interrupts.set(entry.interruptId, {\n ...existing,\n status: 'resolved',\n resolvedAt,\n response: entry.response,\n })\n } else {\n this.interrupts.set(entry.interruptId, {\n ...existing,\n status: 'cancelled',\n resolvedAt,\n })\n }\n }\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()]\n .filter((a) => a.runId === runId)\n .sort(\n (a, b) =>\n a.createdAt - b.createdAt ||\n compareUtf8Bytes(a.artifactId, b.artifactId),\n ),\n )\n }\n listForThread(threadId: string): Promise<Array<ArtifactRecord>> {\n return Promise.resolve(\n [...this.artifacts.values()]\n .filter((a) => a.threadId === threadId)\n .sort(\n (a, b) =>\n a.createdAt - b.createdAt ||\n compareUtf8Bytes(a.artifactId, b.artifactId),\n ),\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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"}
|
|
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 InterruptCommitEntry,\n InterruptRecord,\n InterruptStore,\n MessageStore,\n MetadataStore,\n RunRecord,\n RunStore,\n} from './types'\n\nconst compareUtf8Bytes = (left: string, right: string): number => {\n const leftBytes = new TextEncoder().encode(left)\n const rightBytes = new TextEncoder().encode(right)\n const length = Math.min(leftBytes.length, rightBytes.length)\n\n for (let index = 0; index < length; index++) {\n const leftByte = leftBytes[index]\n const rightByte = rightBytes[index]\n if (leftByte !== rightByte) {\n return (leftByte ?? 0) - (rightByte ?? 0)\n }\n }\n\n return leftBytes.length - rightBytes.length\n}\n\nclass MemoryMessageStore implements MessageStore {\n private readonly threads = new Map<string, Array<ModelMessage>>()\n loadThread(\n threadId: string,\n _options?: { limit?: number; before?: string },\n ): 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 parentRunId?: string\n subagentRunId?: string\n name?: string\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 ...(input.parentRunId !== undefined\n ? { parentRunId: input.parentRunId }\n : {}),\n ...(input.subagentRunId !== undefined\n ? { subagentRunId: input.subagentRunId }\n : {}),\n ...(input.name !== undefined ? { name: input.name } : {}),\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 listByParentRun(parentRunId: string): Promise<Array<RunRecord>> {\n const matching = [...this.runs.values()]\n .filter((run) => run.parentRunId === parentRunId)\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 async commitBatch(\n entries: ReadonlyArray<InterruptCommitEntry>,\n ): Promise<void> {\n const ids = new Set<string>()\n for (const entry of entries) {\n if (ids.has(entry.interruptId)) {\n throw new Error(\n `Interrupt batch contains duplicate id: ${entry.interruptId}.`,\n )\n }\n ids.add(entry.interruptId)\n const existing = this.interrupts.get(entry.interruptId)\n if (!existing) {\n throw new Error(\n `Interrupt batch references missing id: ${entry.interruptId}.`,\n )\n }\n if (existing.status !== 'pending') {\n throw new Error(\n `Interrupt batch references non-pending id: ${entry.interruptId}.`,\n )\n }\n }\n const resolvedAt = Date.now()\n for (const entry of entries) {\n const existing = this.interrupts.get(entry.interruptId)\n if (!existing) continue\n if (entry.status === 'resolved') {\n this.interrupts.set(entry.interruptId, {\n ...existing,\n status: 'resolved',\n resolvedAt,\n response: entry.response,\n })\n } else {\n this.interrupts.set(entry.interruptId, {\n ...existing,\n status: 'cancelled',\n resolvedAt,\n })\n }\n }\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()]\n .filter((a) => a.runId === runId)\n .sort(\n (a, b) =>\n a.createdAt - b.createdAt ||\n compareUtf8Bytes(a.artifactId, b.artifactId),\n ),\n )\n }\n listForThread(threadId: string): Promise<Array<ArtifactRecord>> {\n return Promise.resolve(\n [...this.artifacts.values()]\n .filter((a) => a.threadId === threadId)\n .sort(\n (a, b) =>\n a.createdAt - b.createdAt ||\n compareUtf8Bytes(a.artifactId, b.artifactId),\n ),\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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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { ModelMessage } from '@tanstack/ai';
|
|
2
|
+
export declare function mergeStoredMessages(stored: ReadonlyArray<ModelMessage>, incoming: ReadonlyArray<ModelMessage>): ModelMessage<string | import('@tanstack/ai').ContentPart<unknown, unknown, unknown, unknown, unknown>[] | null>[];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/merge-stored.ts
|
|
2
|
+
function mergeStoredMessages(stored, incoming) {
|
|
3
|
+
if (incoming.length === 0) return stored.slice();
|
|
4
|
+
let cutoff = stored.length;
|
|
5
|
+
for (let index = incoming.length - 1; index >= 0; index--) {
|
|
6
|
+
const id = incoming[index]?.id;
|
|
7
|
+
if (id === void 0) continue;
|
|
8
|
+
const storedIndex = stored.findIndex((message) => message.id === id);
|
|
9
|
+
if (storedIndex >= 0) {
|
|
10
|
+
cutoff = storedIndex + 1;
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const prefix = stored.slice(0, cutoff);
|
|
15
|
+
const incomingById = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const message of incoming) {
|
|
17
|
+
const id = message.id;
|
|
18
|
+
if (id) incomingById.set(id, message);
|
|
19
|
+
}
|
|
20
|
+
const storedIds = /* @__PURE__ */ new Set();
|
|
21
|
+
const merged = [];
|
|
22
|
+
for (const message of prefix) {
|
|
23
|
+
const id = message.id;
|
|
24
|
+
if (id) {
|
|
25
|
+
storedIds.add(id);
|
|
26
|
+
merged.push(incomingById.get(id) ?? message);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
merged.push(message);
|
|
30
|
+
}
|
|
31
|
+
for (let index = 0; index < incoming.length; index++) {
|
|
32
|
+
const message = incoming[index];
|
|
33
|
+
if (!message) continue;
|
|
34
|
+
const id = message.id;
|
|
35
|
+
if (id && storedIds.has(id)) continue;
|
|
36
|
+
if (!id) {
|
|
37
|
+
const existing = merged[index];
|
|
38
|
+
if (existing && existing.id === void 0 && existing.role === message.role && existing.content === message.content) continue;
|
|
39
|
+
}
|
|
40
|
+
merged.push(message);
|
|
41
|
+
}
|
|
42
|
+
return merged;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { mergeStoredMessages };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=merge-stored.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-stored.js","names":[],"sources":["../../src/merge-stored.ts"],"sourcesContent":["import type { ModelMessage } from '@tanstack/ai'\n\n// Empty incoming keeps stored. Non-empty: the last incoming id that already\n// exists in stored is a cutoff (reload drops the old assistant after that\n// user). Same id is replaced in place. New ids and messages with no id are\n// appended.\nexport function mergeStoredMessages(\n stored: ReadonlyArray<ModelMessage>,\n incoming: ReadonlyArray<ModelMessage>,\n) {\n if (incoming.length === 0) {\n return stored.slice()\n }\n\n let cutoff = stored.length\n for (let index = incoming.length - 1; index >= 0; index--) {\n const id = incoming[index]?.id\n if (id === undefined) continue\n const storedIndex = stored.findIndex((message) => message.id === id)\n if (storedIndex >= 0) {\n cutoff = storedIndex + 1\n break\n }\n }\n const prefix = stored.slice(0, cutoff)\n\n const incomingById = new Map<string, ModelMessage>()\n for (const message of incoming) {\n const id = message.id\n if (id) incomingById.set(id, message)\n }\n\n const storedIds = new Set<string>()\n const merged: Array<ModelMessage> = []\n for (const message of prefix) {\n const id = message.id\n if (id) {\n storedIds.add(id)\n merged.push(incomingById.get(id) ?? message)\n continue\n }\n merged.push(message)\n }\n\n for (let index = 0; index < incoming.length; index++) {\n const message = incoming[index]\n if (!message) continue\n const id = message.id\n if (id && storedIds.has(id)) continue\n // Attach/reload can post the stored transcript again with no ids. Keep the\n // prefix row instead of appending a second copy of the same turn.\n if (!id) {\n const existing = merged[index]\n if (\n existing &&\n existing.id === undefined &&\n existing.role === message.role &&\n existing.content === message.content\n ) {\n continue\n }\n }\n merged.push(message)\n }\n\n return merged\n}\n"],"mappings":";AAMA,SAAgB,oBACd,QACA,UACA;CACA,IAAI,SAAS,WAAW,GACtB,OAAO,OAAO,MAAM;CAGtB,IAAI,SAAS,OAAO;CACpB,KAAK,IAAI,QAAQ,SAAS,SAAS,GAAG,SAAS,GAAG,SAAS;EACzD,MAAM,KAAK,SAAS,MAAM,EAAE;EAC5B,IAAI,OAAO,KAAA,GAAW;EACtB,MAAM,cAAc,OAAO,WAAW,YAAY,QAAQ,OAAO,EAAE;EACnE,IAAI,eAAe,GAAG;GACpB,SAAS,cAAc;GACvB;EACF;CACF;CACA,MAAM,SAAS,OAAO,MAAM,GAAG,MAAM;CAErC,MAAM,+BAAe,IAAI,IAA0B;CACnD,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,KAAK,QAAQ;EACnB,IAAI,IAAI,aAAa,IAAI,IAAI,OAAO;CACtC;CAEA,MAAM,4BAAY,IAAI,IAAY;CAClC,MAAM,SAA8B,CAAC;CACrC,KAAK,MAAM,WAAW,QAAQ;EAC5B,MAAM,KAAK,QAAQ;EACnB,IAAI,IAAI;GACN,UAAU,IAAI,EAAE;GAChB,OAAO,KAAK,aAAa,IAAI,EAAE,KAAK,OAAO;GAC3C;EACF;EACA,OAAO,KAAK,OAAO;CACrB;CAEA,KAAK,IAAI,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS;EACpD,MAAM,UAAU,SAAS;EACzB,IAAI,CAAC,SAAS;EACd,MAAM,KAAK,QAAQ;EACnB,IAAI,MAAM,UAAU,IAAI,EAAE,GAAG;EAG7B,IAAI,CAAC,IAAI;GACP,MAAM,WAAW,OAAO;GACxB,IACE,YACA,SAAS,OAAO,KAAA,KAChB,SAAS,SAAS,QAAQ,QAC1B,SAAS,YAAY,QAAQ,SAE7B;EAEJ;EACA,OAAO,KAAK,OAAO;CACrB;CAEA,OAAO;AACT"}
|
package/dist/esm/middleware.d.ts
CHANGED
|
@@ -194,7 +194,8 @@ export interface WithPersistenceOptions {
|
|
|
194
194
|
snapshotStreaming?: boolean;
|
|
195
195
|
/**
|
|
196
196
|
* Minimum milliseconds between streaming snapshots when `snapshotStreaming`
|
|
197
|
-
* is on. Defaults to 1000.
|
|
197
|
+
* is on. Defaults to 1000. A streaming subagent child always uses the same
|
|
198
|
+
* interval for its transcript writes, with or without `snapshotStreaming`.
|
|
198
199
|
*/
|
|
199
200
|
snapshotIntervalMs?: number;
|
|
200
201
|
}
|
package/dist/esm/middleware.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { validateChatPersistenceStores, validateGenerationPersistenceStores } from "./types.js";
|
|
2
2
|
import { InterruptsCapability, PersistenceCapability, PersistenceCompletionCapability, provideInterrupts, providePersistence, providePersistenceCompletion } from "./capabilities.js";
|
|
3
|
+
import { mergeStoredMessages } from "./merge-stored.js";
|
|
4
|
+
import { createSubagentRunRecorder } from "./subagent-runs.js";
|
|
3
5
|
import { artifactBlobKey } from "./retrieve.js";
|
|
4
6
|
import { InterruptResumeValidationError, MetadataCapability, defineChatMiddleware, fromSpecTokenUsage, getDetachableRun, provideMetadata, readInterruptBinding, validateInterruptResumeBatch, wasCancelRequested } from "@tanstack/ai";
|
|
5
7
|
import { createInterruptBinding, getGenericInterruptDefinitionRegistry, providePendingTurn, rehydrateInterruptRequest, toRunErrorPayload } from "@tanstack/ai/adapter-internals";
|
|
@@ -970,48 +972,6 @@ function detachableRun(ctx) {
|
|
|
970
972
|
function threadMessages(loaded) {
|
|
971
973
|
return Array.isArray(loaded) ? loaded : loaded.messages;
|
|
972
974
|
}
|
|
973
|
-
function mergeStoredMessages(stored, incoming) {
|
|
974
|
-
if (incoming.length === 0) return stored.slice();
|
|
975
|
-
let cutoff = stored.length;
|
|
976
|
-
for (let index = incoming.length - 1; index >= 0; index--) {
|
|
977
|
-
const id = incoming[index]?.id;
|
|
978
|
-
if (id === void 0) continue;
|
|
979
|
-
const storedIndex = stored.findIndex((message) => message.id === id);
|
|
980
|
-
if (storedIndex >= 0) {
|
|
981
|
-
cutoff = storedIndex + 1;
|
|
982
|
-
break;
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
const prefix = stored.slice(0, cutoff);
|
|
986
|
-
const incomingById = /* @__PURE__ */ new Map();
|
|
987
|
-
for (const message of incoming) {
|
|
988
|
-
const id = message.id;
|
|
989
|
-
if (id) incomingById.set(id, message);
|
|
990
|
-
}
|
|
991
|
-
const storedIds = /* @__PURE__ */ new Set();
|
|
992
|
-
const merged = [];
|
|
993
|
-
for (const message of prefix) {
|
|
994
|
-
const id = message.id;
|
|
995
|
-
if (id) {
|
|
996
|
-
storedIds.add(id);
|
|
997
|
-
merged.push(incomingById.get(id) ?? message);
|
|
998
|
-
continue;
|
|
999
|
-
}
|
|
1000
|
-
merged.push(message);
|
|
1001
|
-
}
|
|
1002
|
-
for (let index = 0; index < incoming.length; index++) {
|
|
1003
|
-
const message = incoming[index];
|
|
1004
|
-
if (!message) continue;
|
|
1005
|
-
const id = message.id;
|
|
1006
|
-
if (id && storedIds.has(id)) continue;
|
|
1007
|
-
if (!id) {
|
|
1008
|
-
const existing = merged[index];
|
|
1009
|
-
if (existing && existing.id === void 0 && existing.role === message.role && existing.content === message.content) continue;
|
|
1010
|
-
}
|
|
1011
|
-
merged.push(message);
|
|
1012
|
-
}
|
|
1013
|
-
return merged;
|
|
1014
|
-
}
|
|
1015
975
|
/**
|
|
1016
976
|
* Chat-only **state** persistence middleware. Provides durable transcript,
|
|
1017
977
|
* run records, and interrupts for `chat()`. Does **not** provide locks —
|
|
@@ -1048,8 +1008,15 @@ function withPersistence(persistence, options = {}) {
|
|
|
1048
1008
|
...persistence.stores.metadata ? [MetadataCapability] : [],
|
|
1049
1009
|
...wantsInterrupts ? [InterruptsCapability] : []
|
|
1050
1010
|
];
|
|
1011
|
+
const subagentRuns = createSubagentRunRecorder({
|
|
1012
|
+
messages: messageStore,
|
|
1013
|
+
runs,
|
|
1014
|
+
intervalMs: snapshotIntervalMs,
|
|
1015
|
+
...wantsInterrupts && persistence.stores.interrupts ? { interrupts: persistence.stores.interrupts } : {}
|
|
1016
|
+
});
|
|
1051
1017
|
return defineChatMiddleware({
|
|
1052
1018
|
name: "chat-persistence",
|
|
1019
|
+
routedSubagentPersistence: subagentRuns,
|
|
1053
1020
|
provides,
|
|
1054
1021
|
setup(ctx) {
|
|
1055
1022
|
providePersistence(ctx, persistence);
|
|
@@ -1073,7 +1040,8 @@ function withPersistence(persistence, options = {}) {
|
|
|
1073
1040
|
providePersistenceCompletion(ctx, { waitForRunCompletion: () => completion });
|
|
1074
1041
|
if (wantsInterrupts && persistence.stores.interrupts) provideInterrupts(ctx, persistence.stores.interrupts);
|
|
1075
1042
|
providePendingTurn(ctx, { snapshot: async () => {
|
|
1076
|
-
const
|
|
1043
|
+
const stored = threadMessages(await messageStore.loadThread(ctx.threadId));
|
|
1044
|
+
const list = mergeStoredMessages(stored, ctx.messages);
|
|
1077
1045
|
await messageStore.saveThread(ctx.threadId, list);
|
|
1078
1046
|
} });
|
|
1079
1047
|
},
|
|
@@ -1101,7 +1069,8 @@ function withPersistence(persistence, options = {}) {
|
|
|
1101
1069
|
if (state && storedUsage) state.usage = storedUsage;
|
|
1102
1070
|
if (!state?.merged) {
|
|
1103
1071
|
if (state) state.merged = true;
|
|
1104
|
-
|
|
1072
|
+
const stored = threadMessages(await messageStore.loadThread(ctx.threadId));
|
|
1073
|
+
patch.messages = mergeStoredMessages(stored, config.messages);
|
|
1105
1074
|
}
|
|
1106
1075
|
return Object.keys(patch).length > 0 ? patch : void 0;
|
|
1107
1076
|
},
|
|
@@ -1111,6 +1080,11 @@ function withPersistence(persistence, options = {}) {
|
|
|
1111
1080
|
} catch {}
|
|
1112
1081
|
},
|
|
1113
1082
|
async onChunk(ctx, chunk) {
|
|
1083
|
+
await subagentRuns.chunk({
|
|
1084
|
+
threadId: ctx.threadId,
|
|
1085
|
+
runId: ctx.runId,
|
|
1086
|
+
chunk
|
|
1087
|
+
});
|
|
1114
1088
|
if (snapshotStreaming && ctx.phase === "modelStream") {
|
|
1115
1089
|
const s = runState.get(ctx);
|
|
1116
1090
|
if (s && chunk.type === "TEXT_MESSAGE_START") {
|