Skip to content

refactor(persistence)!: trim the public API surface to what apps call - #1034

Merged
AlemTuzlak merged 3 commits into
mainfrom
refactor/trim-persistence-public-surface
Jul 31, 2026
Merged

refactor(persistence)!: trim the public API surface to what apps call#1034
AlemTuzlak merged 3 commits into
mainfrom
refactor/trim-persistence-public-surface

Conversation

@AlemTuzlak

@AlemTuzlak AlemTuzlak commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

🎯 Changes

An audit of everything the persistence work (#984, #1011, #1004, #955) added to the public API turned up a set of exports with no consumer anywhere in the repo: docs, examples, e2e, or the packages themselves. Most of it is the options-and-types half of the client-managed generation mode that #1011 refactored away mid-PR, leaving the supporting surface behind.

This is deletion only. No behavior changes.

Removed from @tanstack/ai-client and the framework packages

  • initialResumeSnapshot is gone from every generation hook (7 hooks × 5 frameworks) and from GenerationClient / VideoGenerationClient. It seeded the storage mode that no longer exists; a run is restored by persistence: true plus a hydrateGeneration handler. useChat keeps its own initialResumeSnapshot.
  • Unexported (internals of the hydration path): GenerationResumeSnapshot, GenerationResumeState, GenerationResumeStatus, GenerationResultSnapshot, GenerationErrorSnapshot, GenerationEventSnapshot, parseGenerationResumeSnapshot, updateGenerationResumeSnapshot, ChatResumeSnapshot.
  • Deleted: GenerationPendingArtifact (a bare alias with one internal use) and GenerationPersistenceOption (a public alias for boolean; persistence?: boolean is inline now). GenerationPersistenceOptions, the union that requires a threadId alongside persistence, is unchanged.
  • ChatResumeSnapshotV1 / ChatResumeSnapshotV2 collapsed into one ChatResumeSnapshot with no schemaVersion. The two were structurally identical, no reader branched on the version, and only V2 was ever written.
  • TValue dropped from localStoragePersistence / sessionStoragePersistence / indexedDBPersistence (and WebStoragePersistenceOptions). The parameter existed so generation could share the chat adapters; there are zero non-default instantiations in the repo.
  • PersistedArtifactRef re-export dropped from the 5 framework packages, where no local type refers to it. It stays public on @tanstack/ai, where docs and the sqlite example import it.

Removed from @tanstack/ai-persistence

  • artifactBlobKey is internal. resolveArtifactBlobKey is the read path its own JSDoc already recommended, since a record written with a custom storageKey carries its real key in blobKey.
  • createInterruptController / InterruptController deleted. A five-method forwarder to stores.interrupts with no caller outside its own test.

Tests

The five initialResumeSnapshot-seeded "no auto-fire on mount" guards were split by whether the coverage still existed elsewhere. The base-hook one is redundant with each framework's existing "hydrated running snapshot with no joinRun surfaces as interrupted" test, so it's deleted. The video one had no hydration-path equivalent, so it's converted to persistence: true + hydrateGeneration and keeps the coverage. The interrupt-controller tests were assertions about stores.interrupts behavior already covered directly by tests/interrupts.test.ts.

Docs

  • Documented the ctx-capability plumbing (PersistenceCapability, InterruptsCapability, getPersistence, providePersistence, getInterrupts, provideInterrupts) in persistence/internals. It is legitimate surface for third-party middleware that needs the stores withPersistence holds, and had zero mentions in docs.

  • Fixed the ai-core/client-persistence skill frontmatter, which still advertised the removed client-driven generation mode and generation:<threadId> storage keys while the skill's own body is titled "Generation hooks: server-driven only". The frontmatter is what routes an agent to the skill, so it was the worst place for it to be wrong.

  • Split build-your-own-adapter.md (1513 lines, three reader journeys in one page) into four, keeping the original path so every inbound link survives:

    • build-your-own-adapter (337): what an adapter is, which stores you need, mapping onto an existing schema, the conformance suite, the agent skills
    • build-your-own-chat-adapter (436): the SQLite walkthrough for the four chat stores
    • build-your-own-generation-adapter (517): generation runs, artifacts, blobs
    • store-reference (286): the seven store contracts

    Anchors into the moved sections (#generation--media-stores, #store-interface-reference) are repointed from controls, generation-persistence, keep-generated-files, internals, and overview; nav has the three new entries.

  • Removed all 96 em dashes and the · separators from docs/persistence/**, rewritten sentence by sentence rather than swapped for another glyph.

Changesets

New trim-persistence-public-surface.md, plus six pending changesets reconciled where they described a removed API as surviving (generation-persistence-server-only claimed initialResumeSnapshot was "unchanged"; client-typed-storage-adapter-defaults was entirely about the TValue default that no longer exists).

Known leak left in place

GenerationClient.getResumeSnapshot() / VideoGenerationClient.getResumeSnapshot() are still public methods returning the now-internal snapshot type, and only tests call them. Removing them means rewriting ~14 assertions in generation-client.test.ts; happy to fold that in if wanted.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

test:types (68/68), test:lib, build, test:build, test:docs, test:kiira (875 snippets), test:oxlint, test:knip, test:sherif, test:dts, the react-native smoke, and the E2E suite (387 passed, 11 flaky TTS click timeouts that pass on retry) are all green locally. One unrelated pre-existing failure on Windows: packages/ai-sandbox/tests/harness-cwd.test.ts asserts \tmp\... against /tmp/..., a path-separator bug in the test itself.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Breaking Changes

    • Generation persistence is now server-only and requires persistence: true with a thread ID.
    • Generation hooks no longer accept initial resume snapshots; active runs are exposed through runId.
    • Deprecated snapshot types, interrupt controllers, and artifact-key exports were removed.
    • Storage adapters now target chat persisted state directly.
    • Generation run records and hydration state now require a thread ID.
  • Documentation

    • Added chat and generation adapter guides and a comprehensive store reference.
    • Clarified hydration, validation, artifact resolution, and persistence requirements.
    • Direct generation clients must mount Devtools to trigger hydration.

Generation persistence is server-driven, so the options and types that only
existed to support a client-managed copy of a run are gone, along with a few
exports no consumer ever named.

- remove `initialResumeSnapshot` from every generation hook (7 hooks x 5
  frameworks) and from `GenerationClient` / `VideoGenerationClient`; `useChat`
  keeps its own
- unexport the generation hydration internals from `@tanstack/ai-client`:
  `GenerationResumeSnapshot` / `GenerationResumeState` /
  `GenerationResumeStatus` / `GenerationResultSnapshot` /
  `GenerationErrorSnapshot` / `GenerationEventSnapshot` /
  `parseGenerationResumeSnapshot` / `updateGenerationResumeSnapshot` /
  `ChatResumeSnapshot`
- delete `GenerationPendingArtifact` and `GenerationPersistenceOption` (an alias
  for `boolean`)
- collapse `ChatResumeSnapshotV1` / `ChatResumeSnapshotV2` into one shape with no
  `schemaVersion`: they were structurally identical, no reader branched on the
  version, and only V2 was written
- drop the `TValue` generic from `localStoragePersistence` /
  `sessionStoragePersistence` / `indexedDBPersistence`; it existed so generation
  could share the chat adapters and had zero non-default instantiations
- stop re-exporting `PersistedArtifactRef` from the framework packages, where no
  hook type refers to it
- unexport `artifactBlobKey` in favour of `resolveArtifactBlobKey`, which its own
  docs already recommended for reads
- delete `createInterruptController` / `InterruptController`, a five-method
  forwarder to the `interrupts` store with no caller outside its own test

Tests: the base-hook "no auto-fire from a seeded running snapshot" guards are
redundant with each framework's existing hydrated-running test and are removed;
the video ones are converted to the surviving `persistence: true` +
`hydrateGeneration` path so the coverage stays.

Docs:
- document the ctx-capability plumbing (`PersistenceCapability`,
  `InterruptsCapability`, `getPersistence`, `getInterrupts`,
  `providePersistence`, `provideInterrupts`) in persistence/internals
- fix the `ai-core/client-persistence` skill frontmatter, which still described
  the removed client-driven generation mode its own body denies
- split the 1513-line build-your-own-adapter page into build-your-own-adapter
  (shape, store choice, existing schemas, conformance), build-your-own-chat-adapter,
  build-your-own-generation-adapter, and store-reference; inbound links and
  anchors repointed, nav updated
- drop every em dash and separator glyph from docs/persistence
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4da27119-ac23-403f-a421-3eb13ba712ba

📥 Commits

Reviewing files that changed from the base of the PR and between e5537f6 and c2f02d8.

📒 Files selected for processing (2)
  • docs/persistence/build-your-own-adapter.md
  • docs/persistence/store-reference.md

📝 Walkthrough

Walkthrough

The PR narrows generation persistence to server hydration, removes public generation resume APIs, unifies chat snapshots, limits storage adapters to chat state, and adds separate persistence adapter and store-reference guides.

Changes

Persistence API and hydration

Layer / File(s) Summary
Core persistence contracts and exports
packages/ai-client/..., packages/ai-persistence/..., .changeset/*
Generation resume APIs and client persistence options are removed or internalized. Chat snapshots use one unversioned shape. Storage factories target ChatPersistedState.
Framework generation wiring
packages/ai-angular/..., packages/ai-react/..., packages/ai-solid/..., packages/ai-svelte/..., packages/ai-vue/...
Generation hooks remove initialResumeSnapshot, initialize runId to null, and use server hydration and run-joining handlers.
Hydration validation tests
packages/ai-*/tests/*
Tests verify that unjoinable running generations become interrupted errors without reconnecting or restarting.
Adapter guides and store contracts
docs/persistence/build-your-own-*.md, docs/persistence/store-reference.md
New SQLite examples document chat stores, generation stores, artifact storage, blob storage, assembly, and endpoint integration.
Persistence navigation and guidance
docs/config.json, docs/persistence/*.md, packages/ai/skills/..., packages/ai-persistence/skills/...
Documentation updates add navigation, capability guidance, server hydration wording, and artifact-key guidance.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • TanStack/ai#984: Related client persistence adapters, persisted-state types, and resume snapshot handling.
  • TanStack/ai#997: Related generation persistence and resume snapshot APIs.
  • TanStack/ai#999: Related generation client, hook, and storage adapter changes.

Suggested labels: persistence

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary change: reducing the public persistence API surface.
Description check ✅ Passed The description explains the changes, motivation, testing, release impact, and known limitation, with all required template sections completed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/trim-persistence-public-surface

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ai-client/src/generation-client.ts (1)

145-198: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Prevent server hydration during Svelte setup. Both clients rely on mountDevtools() being called after client mount, but the supplied Svelte factories call it during setup. This can invoke hydrateGeneration during SSR after client-managed snapshot seeds are removed.

  • packages/ai-client/src/generation-client.ts#L145-L198: update the Svelte generation wiring so it calls mountDevtools() from onMount, not setup.
  • packages/ai-client/src/video-generation-client.ts#L149-L191: update the Svelte video-generation wiring so it calls mountDevtools() from onMount, not setup.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-client/src/generation-client.ts` around lines 145 - 198, Update
the Svelte generation wiring in packages/ai-client/src/generation-client.ts
around the GenerationClient setup to invoke mountDevtools from Svelte’s onMount
callback rather than during setup. Apply the same change to the Svelte
video-generation wiring in packages/ai-client/src/video-generation-client.ts
around the video client setup; both files must defer mountDevtools until
onMount.
🧹 Nitpick comments (2)
packages/ai-solid/tests/use-generation.test.ts (1)

83-86: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Place the unit test beside the covered source.

This *.test.ts file is under packages/ai-solid/tests/, but it covers packages/ai-solid/src/use-generation.ts. Move the test beside the source module.

As per coding guidelines, files matching **/*.test.ts must place unit tests alongside the source they cover.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-solid/tests/use-generation.test.ts` around lines 83 - 86, Move
the use-generation unit test file from the package-level tests directory to the
directory containing the covered use-generation source module, keeping the test
contents and behavior unchanged.

Source: Coding guidelines

packages/ai-react/tests/use-generation.test.ts (1)

1024-1049: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Move the changed hydration tests beside their source modules.

Both changed tests remain in package-level tests/ directories. Place each test in a colocated *.test.ts file.

  • packages/ai-react/tests/use-generation.test.ts#L1024-L1049: move the useGenerateVideo hydration test beside packages/ai-react/src/use-generate-video.ts.
  • packages/ai-svelte/tests/create-generation.test.ts#L844-L864: move the createGenerateVideo hydration test beside packages/ai-svelte/src/create-generate-video.svelte.ts.

As per coding guidelines, “Place unit tests in *.test.ts files alongside the source they cover.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-react/tests/use-generation.test.ts` around lines 1024 - 1049,
Move the useGenerateVideo hydration test from
packages/ai-react/tests/use-generation.test.ts#L1024-L1049 into a colocated
*.test.ts file beside packages/ai-react/src/use-generate-video.ts, and move the
createGenerateVideo hydration test from
packages/ai-svelte/tests/create-generation.test.ts#L844-L864 into a colocated
*.test.ts file beside packages/ai-svelte/src/create-generate-video.svelte.ts;
preserve both tests unchanged apart from required imports and setup adjustments.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/persistence/build-your-own-adapter.md`:
- Around line 286-294: Update the “Let your coding agent write it” section to
scope the workflow to the selected backend: describe extending the existing
persistence stack for raw pg, Kysely, Mongo, and Supabase, and limit the ORM
configuration/schema and four-table instructions to relational chat skills.

In `@docs/persistence/build-your-own-chat-adapter.md`:
- Around line 155-165: The createOrResume implementation in
docs/persistence/build-your-own-chat-adapter.md lines 155-165 must re-read the
run by input.runId after insert.run and return it through mapRun, so the result
reflects the record that won a concurrent insert. Apply the same change to
docs/persistence/build-your-own-generation-adapter.md lines 142-163: re-read the
stored generation run after insert.run and map that record instead of returning
caller-supplied fields.
- Around line 81-87: Validate every rehydrated persisted value before returning
it: in docs/persistence/build-your-own-chat-adapter.md lines 81-87, validate the
complete ModelMessage[] shape in loadThread; lines 128-140, validate parsed
usage and required run fields before returning RunRecord; lines 239-251,
validate interrupt payloads and parsed responses; and lines 346-349, apply safe
parsing and validation to metadata values. In
docs/persistence/build-your-own-generation-adapter.md lines 104-125, validate
generation error, result, artifact, usage, and required scalar fields; and lines
345-355, validate customMetadata before returning BlobRecord. Preserve
unknown/missing-value behavior while preventing malformed JSON or
valid-but-wrong-shaped data from crossing adapter boundaries.

In `@docs/persistence/client-persistence.md`:
- Around line 193-194: Update the custom persistence adapter’s rehydration guard
for ChatPersistedState to validate every message element and the complete
resumeState structure, plus optional pendingInterrupts, before getItem returns
the record. Replace the shallow messages-array and unknown resume checks with
the existing complete type guard or Standard Schema parser, while preserving
valid persisted records.

In `@docs/persistence/generation-persistence.md`:
- Line 53: Rewrite the lifecycle description at line 53 as complete sentences
instead of comma-separated fragments, and update the “Status running” text at
line 428 to be a complete sentence. Preserve the original meaning while
improving readability in both locations.

In `@docs/persistence/internals.md`:
- Around line 186-187: Update the adapter guide description in the referenced
documentation text to replace “end to end” with clearer wording such as “the
complete SQLite flow,” while preserving the existing link and meaning.

In `@packages/ai-client/src/types.ts`:
- Around line 40-50: Update readResumeState() and persistResumeSnapshot() to
explicitly handle legacy ChatPersistedState.resume records with schemaVersion: 1
before applying them, migrating them into the current ChatResumeSnapshot shape
or intentionally invalidating them. Ensure invalid legacy data cannot be
silently treated as a valid resume state. Clarify the ChatResumeSnapshot
documentation and visibility to reflect whether this snapshot shape is a stable
API contract.

In
`@packages/ai-persistence/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md`:
- Around line 348-349: Update the helper description in the
build-cloudflare-artifact-store skill to refer only to retrieveBlob, or
explicitly state that blob reads resolve the key; do not imply retrieveArtifact
performs blob-key resolution.

In `@packages/ai-solid/tests/use-generation.test.ts`:
- Around line 83-86: Update the hydrateGeneration mock and related assertions in
the test to verify it receives the expected thread ID, “video-no-auto-fire,”
rather than ignoring the argument. Return a hydrated snapshot whose
resumeState.threadId matches that requested ID, while preserving the existing
resume behavior checks.

---

Outside diff comments:
In `@packages/ai-client/src/generation-client.ts`:
- Around line 145-198: Update the Svelte generation wiring in
packages/ai-client/src/generation-client.ts around the GenerationClient setup to
invoke mountDevtools from Svelte’s onMount callback rather than during setup.
Apply the same change to the Svelte video-generation wiring in
packages/ai-client/src/video-generation-client.ts around the video client setup;
both files must defer mountDevtools until onMount.

---

Nitpick comments:
In `@packages/ai-react/tests/use-generation.test.ts`:
- Around line 1024-1049: Move the useGenerateVideo hydration test from
packages/ai-react/tests/use-generation.test.ts#L1024-L1049 into a colocated
*.test.ts file beside packages/ai-react/src/use-generate-video.ts, and move the
createGenerateVideo hydration test from
packages/ai-svelte/tests/create-generation.test.ts#L844-L864 into a colocated
*.test.ts file beside packages/ai-svelte/src/create-generate-video.svelte.ts;
preserve both tests unchanged apart from required imports and setup adjustments.

In `@packages/ai-solid/tests/use-generation.test.ts`:
- Around line 83-86: Move the use-generation unit test file from the
package-level tests directory to the directory containing the covered
use-generation source module, keeping the test contents and behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c72cbabe-3e53-416f-9ace-d0c7e224c369

📥 Commits

Reviewing files that changed from the base of the PR and between bef85d2 and 76aff62.

📒 Files selected for processing (85)
  • .changeset/client-typed-storage-adapter-defaults.md
  • .changeset/generation-mount-hydration-and-speech-restore.md
  • .changeset/generation-persistence-server-only.md
  • .changeset/generation-persistence.md
  • .changeset/generation-run-threadid-required.md
  • .changeset/hooks-expose-run-id.md
  • .changeset/trim-persistence-public-surface.md
  • docs/config.json
  • docs/persistence/build-your-own-adapter.md
  • docs/persistence/build-your-own-chat-adapter.md
  • docs/persistence/build-your-own-generation-adapter.md
  • docs/persistence/chat-persistence.md
  • docs/persistence/client-persistence.md
  • docs/persistence/controls.md
  • docs/persistence/generation-persistence.md
  • docs/persistence/id-map.md
  • docs/persistence/internals.md
  • docs/persistence/keep-generated-files.md
  • docs/persistence/overview.md
  • docs/persistence/store-reference.md
  • packages/ai-angular/src/index.ts
  • packages/ai-angular/src/inject-generate-audio.ts
  • packages/ai-angular/src/inject-generate-video.ts
  • packages/ai-angular/src/inject-generation.ts
  • packages/ai-angular/tests/inject-generation.test.ts
  • packages/ai-angular/tests/test-utils.ts
  • packages/ai-client/src/chat-client.ts
  • packages/ai-client/src/connection-adapters.ts
  • packages/ai-client/src/generation-client.ts
  • packages/ai-client/src/generation-types.ts
  • packages/ai-client/src/index.ts
  • packages/ai-client/src/storage-adapters.ts
  • packages/ai-client/src/types.ts
  • packages/ai-client/src/video-generation-client.ts
  • packages/ai-client/tests/chat-client-interrupt-correlation.test.ts
  • packages/ai-client/tests/chat-client-interrupts.test.ts
  • packages/ai-client/tests/resume-snapshot.test.ts
  • packages/ai-persistence/skills/ai-persistence/SKILL.md
  • packages/ai-persistence/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md
  • packages/ai-persistence/src/index.ts
  • packages/ai-persistence/src/interrupts.ts
  • packages/ai-persistence/src/retrieve.ts
  • packages/ai-persistence/tests/capabilities.test.ts
  • packages/ai-preact/tests/test-utils.ts
  • packages/ai-react/src/index.ts
  • packages/ai-react/src/use-generate-audio.ts
  • packages/ai-react/src/use-generate-image.ts
  • packages/ai-react/src/use-generate-speech.ts
  • packages/ai-react/src/use-generate-video.ts
  • packages/ai-react/src/use-generation.ts
  • packages/ai-react/src/use-summarize.ts
  • packages/ai-react/src/use-transcription.ts
  • packages/ai-react/tests/test-utils.ts
  • packages/ai-react/tests/use-generation.test.ts
  • packages/ai-solid/src/index.ts
  • packages/ai-solid/src/use-generate-audio.ts
  • packages/ai-solid/src/use-generate-image.ts
  • packages/ai-solid/src/use-generate-speech.ts
  • packages/ai-solid/src/use-generate-video.ts
  • packages/ai-solid/src/use-generation.ts
  • packages/ai-solid/src/use-summarize.ts
  • packages/ai-solid/src/use-transcription.ts
  • packages/ai-solid/tests/test-utils.ts
  • packages/ai-solid/tests/use-generation.test.ts
  • packages/ai-svelte/src/create-generate-audio.svelte.ts
  • packages/ai-svelte/src/create-generate-image.svelte.ts
  • packages/ai-svelte/src/create-generate-speech.svelte.ts
  • packages/ai-svelte/src/create-generate-video.svelte.ts
  • packages/ai-svelte/src/create-generation.svelte.ts
  • packages/ai-svelte/src/create-summarize.svelte.ts
  • packages/ai-svelte/src/create-transcription.svelte.ts
  • packages/ai-svelte/src/index.ts
  • packages/ai-svelte/tests/create-generation.test.ts
  • packages/ai-svelte/tests/test-utils.ts
  • packages/ai-vue/src/index.ts
  • packages/ai-vue/src/use-generate-audio.ts
  • packages/ai-vue/src/use-generate-image.ts
  • packages/ai-vue/src/use-generate-speech.ts
  • packages/ai-vue/src/use-generate-video.ts
  • packages/ai-vue/src/use-generation.ts
  • packages/ai-vue/src/use-summarize.ts
  • packages/ai-vue/src/use-transcription.ts
  • packages/ai-vue/tests/test-utils.ts
  • packages/ai-vue/tests/use-generation.test.ts
  • packages/ai/skills/ai-core/client-persistence/SKILL.md
💤 Files with no reviewable changes (17)
  • packages/ai-persistence/src/interrupts.ts
  • packages/ai-vue/src/index.ts
  • packages/ai-react/src/use-generate-speech.ts
  • packages/ai-svelte/src/index.ts
  • packages/ai-client/tests/chat-client-interrupt-correlation.test.ts
  • packages/ai-client/tests/chat-client-interrupts.test.ts
  • packages/ai-react/src/use-generate-audio.ts
  • packages/ai-persistence/tests/capabilities.test.ts
  • packages/ai-angular/src/index.ts
  • packages/ai-react/src/use-generate-image.ts
  • packages/ai-react/src/use-summarize.ts
  • packages/ai-solid/src/index.ts
  • packages/ai-react/src/index.ts
  • packages/ai-client/src/chat-client.ts
  • packages/ai-react/src/use-transcription.ts
  • packages/ai-client/tests/resume-snapshot.test.ts
  • packages/ai-persistence/src/index.ts

Comment on lines +286 to +294

## Let your coding agent write it

You do not have to type this page out. `@tanstack/ai-persistence` ships
[Agent Skills](../getting-started/agent-skills) that turn it into a recipe your
assistant follows against **your** stack: it reads your existing ORM config,
schema file, and database handle, appends the four tables to the schema you
already have, and writes a single `src/lib/chat-persistence.ts` exporting the
`ChatPersistence` — no new package, no second database client, and no migration
`ChatPersistence`. No new package, no second database client, and no migration

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Scope the agent instructions to the selected backend.

The text says every skill reads ORM configuration and appends four tables. The same table includes build-custom-adapter for raw pg, Kysely, Mongo, and Supabase. Those backends do not share one table-based workflow. Describe extending the existing persistence stack, or limit the four-table statement to relational chat skills.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/persistence/build-your-own-adapter.md` around lines 286 - 294, Update
the “Let your coding agent write it” section to scope the workflow to the
selected backend: describe extending the existing persistence stack for raw pg,
Kysely, Mongo, and Supabase, and limit the ORM configuration/schema and
four-table instructions to relational chat skills.

Comment on lines +81 to +87
async loadThread(threadId) {
const json = select.get(threadId)?.messages_json
// Unknown thread → [] (never null). `node:sqlite` types columns as a
// SQL-value union, so narrow to string before parsing (no cast).
if (typeof json !== 'string') return []
const parsed: Array<ModelMessage> = JSON.parse(json)
return parsed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Validate persisted JSON at every adapter boundary.

JSON.parse is used as if it were validation. It only validates syntax. Corrupt rows can throw during hydration, and valid JSON can still have the wrong shape.

  • docs/persistence/build-your-own-chat-adapter.md#L81-L87: validate the full ModelMessage[] shape before returning loadThread.
  • docs/persistence/build-your-own-chat-adapter.md#L128-L140: validate parsed usage and required run fields before returning RunRecord.
  • docs/persistence/build-your-own-chat-adapter.md#L239-L251: validate interrupt payloads and parsed responses.
  • docs/persistence/build-your-own-chat-adapter.md#L346-L349: apply the same safe parsing policy to metadata values.
  • docs/persistence/build-your-own-generation-adapter.md#L104-L125: validate generation error, result, artifact, usage, and required scalar fields.
  • docs/persistence/build-your-own-generation-adapter.md#L345-L355: validate customMetadata before returning BlobRecord.

Based on learnings, hand-rolled persistence adapters must validate rehydrated values before returning them.

📍 Affects 2 files
  • docs/persistence/build-your-own-chat-adapter.md#L81-L87 (this comment)
  • docs/persistence/build-your-own-chat-adapter.md#L128-L140
  • docs/persistence/build-your-own-chat-adapter.md#L239-L251
  • docs/persistence/build-your-own-chat-adapter.md#L346-L349
  • docs/persistence/build-your-own-generation-adapter.md#L104-L125
  • docs/persistence/build-your-own-generation-adapter.md#L345-L355
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/persistence/build-your-own-chat-adapter.md` around lines 81 - 87,
Validate every rehydrated persisted value before returning it: in
docs/persistence/build-your-own-chat-adapter.md lines 81-87, validate the
complete ModelMessage[] shape in loadThread; lines 128-140, validate parsed
usage and required run fields before returning RunRecord; lines 239-251,
validate interrupt payloads and parsed responses; and lines 346-349, apply safe
parsing and validation to metadata values. In
docs/persistence/build-your-own-generation-adapter.md lines 104-125, validate
generation error, result, artifact, usage, and required scalar fields; and lines
345-355, validate customMetadata before returning BlobRecord. Preserve
unknown/missing-value behavior while preventing malformed JSON or
valid-but-wrong-shaped data from crossing adapter boundaries.

Source: Learnings

Comment on lines +155 to +165
async createOrResume(input) {
const existing = select.get(input.runId)
if (existing) return mapRun(existing)
const status: RunStatus = input.status ?? 'running'
insert.run(input.runId, input.threadId, status, input.startedAt)
return {
runId: input.runId,
threadId: input.threadId,
status,
startedAt: input.startedAt,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Make createOrResume return the record that won the insert race.

Both implementations read before inserting and then return the caller's input. If another process inserts the same run ID between those operations, ON CONFLICT DO NOTHING ignores the second insert but the function returns unsaved fields.

  • docs/persistence/build-your-own-chat-adapter.md#L155-L165: re-read and map the stored chat run after insert.run.
  • docs/persistence/build-your-own-generation-adapter.md#L142-L163: re-read and map the stored generation run after insert.run.
📍 Affects 2 files
  • docs/persistence/build-your-own-chat-adapter.md#L155-L165 (this comment)
  • docs/persistence/build-your-own-generation-adapter.md#L142-L163
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/persistence/build-your-own-chat-adapter.md` around lines 155 - 165, The
createOrResume implementation in docs/persistence/build-your-own-chat-adapter.md
lines 155-165 must re-read the run by input.runId after insert.run and return it
through mapRun, so the result reflects the record that won a concurrent insert.
Apply the same change to docs/persistence/build-your-own-generation-adapter.md
lines 142-163: re-read the stored generation run after insert.run and map that
record instead of returning caller-supplied fields.

Comment on lines +193 to +194
`{ messages, resume? }` blob per chat id (the transcript plus the pointer that
lets a reload rejoin an in-flight run), so `setItem` receives that whole record,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 '\bChatPersistedState\b|ChatClientPersistence|resume' \
  packages/ai-client/src docs/persistence/client-persistence.md

Repository: TanStack/ai

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docs custom adapter lines =="
sed -n '190,235p' docs/persistence/client-persistence.md

echo "== client persistor normalize/read lines =="
sed -n '1,140p' packages/ai-client/src/client-persistor.ts

echo "== relevant types definitions =="
rg -n -C 3 'export type (ChatPersistedState|ChatResumeSnapshot|ChatClientPersistence)|interface ChatClientPersistence|ChatResumeSnapshot|ResumeSnapshot' packages/ai-client/src packages/ai-client -g '*.ts' -g '*.d.ts'

Repository: TanStack/ai

Length of output: 50370


Validate the full ChatPersistedState shape before rehydration.

The custom adapter’s guard accepts records where messages is an array, even if the array contains arbitrary values. It also accepts any resume: unknown when present. Since the persistor returns this object directly from getItem, use a complete type guard or Standard Schema parser for messages, resumeState, and optional pendingInterrupts before returning the persisted record.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/persistence/client-persistence.md` around lines 193 - 194, Update the
custom persistence adapter’s rehydration guard for ChatPersistedState to
validate every message element and the complete resumeState structure, plus
optional pendingInterrupts, before getItem returns the record. Replace the
shallow messages-array and unknown resume checks with the existing complete type
guard or Standard Schema parser, while preserving valid persisted records.

Source: Learnings

add server byte storage: see [Keep generated files](./keep-generated-files).

The record's lifecycle is small one status field the middleware advances:
The record's lifecycle is small, one status field the middleware advances:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Replace the comma-based fragments with complete sentences.

Line 53 leaves the lifecycle description compressed and difficult to parse. Line 428 leaves Status running as a sentence fragment.

Proposed wording
- The record's lifecycle is small, one status field the middleware advances:
+ The record has a small lifecycle. The middleware advances one status field:

- // Opens the run. Status `running`, and there is no video yet.
+ // Opens the run with status `running`; no video exists yet.

Also applies to: 428-428

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/persistence/generation-persistence.md` at line 53, Rewrite the lifecycle
description at line 53 as complete sentences instead of comma-separated
fragments, and update the “Status running” text at line 428 to be a complete
sentence. Preserve the original meaning while improving readability in both
locations.

Comment on lines +186 to +187
stores it exposes. [Build a chat adapter](./build-your-own-chat-adapter) shows
this end to end for SQLite.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use clearer wording for the adapter guide description.

The phrase end to end reads awkwardly here and is flagged by the static analysis hint. Replace it with a clear phrase such as the complete SQLite flow.

🧰 Tools
🪛 LanguageTool

[grammar] ~187-~187: Use a hyphen to join words.
Context: ...ld-your-own-chat-adapter) shows this end to end for SQLite. composePersistence do...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/persistence/internals.md` around lines 186 - 187, Update the adapter
guide description in the referenced documentation text to replace “end to end”
with clearer wording such as “the complete SQLite flow,” while preserving the
existing link and meaning.

Source: Linters/SAST tools

Comment on lines +40 to 50
/**
* The durable pointer a chat keeps for the run it may need to rejoin, plus any
* interrupt that run is waiting on.
*
* @internal
*/
export interface ChatResumeSnapshot {
resumeState: ChatResumeState
pendingInterrupts?: Array<ChatPendingInterrupt>
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Expect migration or intentional invalidation for old snapshots, plus
# declaration configuration that keeps internal hydration types private.
rg -n -C 5 \
  'ChatResumeSnapshot(V1|V2)?|schemaVersion|getResumeSnapshot|resumeInterruptsUnsafe|stripInternal|api-extractor' \
  .

Repository: TanStack/ai

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate ai-client types and api extractor config =="
git ls-files | rg '(^|/)api-extractor\.json$|packages/ai-client/src/types\.ts|packages/ai-client/package\.json|packages/ai-client/tsconfig.*\.json$|packages/ai-client/dist' || true

echo
echo "== ai-client types relevant lines =="
wc -l packages/ai-client/src/types.ts
sed -n '1,90p' packages/ai-client/src/types.ts
echo "... around lines 550-590 ..."
sed -n '550,600p' packages/ai-client/src/types.ts

echo
echo "== local references to ChatResumeSnapshot in source =="
rg -n "ChatResumeSnapshot" packages/ai-client packages/ai-persistence packages/ai-react packages/ai-vue packages/ai-svelte packages/ai-angular docs examples testing/e2e 2>/dev/null || true

echo
echo "== api extractor / typedoc / tsup relevant files =="
git ls-files | rg 'api-extractor|typedoc|tsup|tsconfig' | sed -n '1,200p'

echo
echo "== package config snippets for ai-client =="
for f in $(git ls-files | rg '(^|/)packages/ai-client/(package|tsup|typedoc|api-extractor).*\.(json|ts|js)$'); do
  echo "--- $f"
  sed -n '1,220p' "$f"
done

echo
echo "== declaration export/stripping scripts/config =="
rg -n "stripInternal|internal|api-extractor|exports|declaration|dts|package.json|api\.json" packages/ai-client packages/ai-persistence config 2>/dev/null || true

Repository: TanStack/ai

Length of output: 19237


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read-only behavioral probe: derive the effective ChatResumeSnapshot shape from source and
# check whether schemaVersion remains present in the direct type or constructor-accepted snapshot.
python3 - <<'PY'
from pathlib import Path
import re

paths = [
    Path("packages/ai-client/src/types.ts"),
    Path("packages/ai-persistence/src/reconstruct-generation.ts"),
]
for p in paths:
    print(f"== {p} ==")
    text = p.read_text()
    for needle in ["ChatResumeSnapshot", "schemaVersion", "resumeSnapshot", "ReconstructedGeneration"]:
        occurs = [i for i, line in enumerate(text.splitlines(), 1) if needle in line]
        print(f"{needle}: occurrences at lines {occurs[:20]}{'...' if len(occurs)>20 else ''} total={len(occurs)}")
PY

Repository: TanStack/ai

Length of output: 748


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== chat-client resume/snapshot methods =="
sed -n '1,80p' packages/ai-client/src/chat-client.ts
sed -n '740,830p' packages/ai-client/src/chat-client.ts

echo
echo "== resume snapshot tests =="
sed -n '1,90p' packages/ai-client/tests/resume-snapshot.test.ts
rg -n -C 4 'schemaVersion|applyResumeSnapshot|applyPersistedResume|persistResumeSnapshot|initialResumeSnapshot|ChatResumeSnapshot' packages/ai-client/tests packages/ai-client/src/chat-client.ts packages/ai-client/src/client-persistor.ts packages/ai-client/src/types.ts

echo
echo "== build config and generated/internal strip sources =="
cat packages/ai-client/tsconfig.json
cat packages/ai-client/vite.config.ts
rg -n --glob '*.{ts,js,json,html}' "stripInternal|internal|declaration|emitDeclarationOnly|vite-plugin-dts|rollup|api-extractor" packages/ai-client packages/ai-client/src packages tests 2>/dev/null | sed -n '1,220p' || true

Repository: TanStack/ai

Length of output: 50367


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== readResumeState implementation surrounding line =="
sed -n '210,255p' packages/ai-client/src/chat-client.ts

echo
echo "== persistor normalize and writeState implementation =="
sed -n '1,180p' packages/ai-client/src/client-persistor.ts
sed -n '180,225p' packages/ai-client/src/client-persistor.ts

echo
echo "== package build scripts and root workspace scripts =="
cat packages/ai-client/package.json
sed -n '1,220p' package.json

echo
echo "== typedoc/doc generation files mentioning internals =="
rg -n "`@internal`|stripInternal" docs packages/tsconfig.docs.json packages/ ai -g '*.{ts,md,json,jsonc,js,html}' 2>/dev/null | sed -n '1,220p' || true

Repository: TanStack/ai

Length of output: 19837


Handle old persisted chat resume snapshots before applying them.

readResumeState() and persistResumeSnapshot() in @tanstack/ai-client now accept a snapshot without schemaVersion, but the persisted generation resume contracts still use schemaVersion: 1. Old ChatPersistedState.resume records may migrate to a different internal shape than @tanstack/ai-client expects, or be silently applied as invalid. Add migration/intentional invalidation for older resume records and document the stable ChatResumeSnapshot contract if @internal does not mean “not API guaranteed”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-client/src/types.ts` around lines 40 - 50, Update
readResumeState() and persistResumeSnapshot() to explicitly handle legacy
ChatPersistedState.resume records with schemaVersion: 1 before applying them,
migrating them into the current ChatResumeSnapshot shape or intentionally
invalidating them. Ensure invalid legacy data cannot be silently treated as a
valid resume state. Clarify the ChatResumeSnapshot documentation and visibility
to reflect whether this snapshot shape is a stable API contract.

Comment on lines +83 to +86
const videoResumeSnapshot = {
schemaVersion: 1 as const,
resumeState: { threadId: 'thread-resume', runId: 'run-resume' },
status: 'running' as const,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Assert the hydration request identity.

hydrateGeneration ignores its threadId argument. The fixture contains resumeState.threadId: 'thread-resume', while the hook requests 'video-no-auto-fire'. This test can pass if the client sends the wrong thread ID. Make the mock assert the argument and return a snapshot with the same thread ID.

Suggested test adjustment
-      const hydrateGeneration = vi.fn(async () => ({
-        resumeSnapshot: videoResumeSnapshot,
+      const hydrateGeneration = vi.fn(async (threadId: string) => ({
+        resumeSnapshot: {
+          ...videoResumeSnapshot,
+          resumeState: {
+            ...videoResumeSnapshot.resumeState,
+            threadId,
+          },
+        },
         activeRun: null,
       }))
...
+      expect(hydrateGeneration).toHaveBeenCalledWith('video-no-auto-fire')

Also applies to: 1390-1400

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ai-solid/tests/use-generation.test.ts` around lines 83 - 86, Update
the hydrateGeneration mock and related assertions in the test to verify it
receives the expected thread ID, “video-no-auto-fire,” rather than ignoring the
argument. Return a hydrated snapshot whose resumeState.threadId matches that
requested ID, while preserving the existing resume behavior checks.

Conflict: docs/persistence/build-your-own-adapter.md. This branch split that page
into build-your-own-adapter / build-your-own-chat-adapter /
build-your-own-generation-adapter / store-reference, while #1033 edited the
sections that moved.

Resolved by keeping the split and porting #1033's changes to their new homes:

- build-your-own-generation-adapter: the SQLite `BlobStore` walkthrough gains
  ranged reads (`resolveBlobRange`, metadata-only `selectMeta`, `substr`-based
  `selectSlice`, `blobObject`'s `range` argument, metadata-only `head`), plus a
  bullet pointing at the `get` contract.
- store-reference: `BlobObject.range`, `BlobPutOptions.expectedLength`,
  `BlobRange`, `BlobGetOptions`, the widened `BlobStore.get` signature, and the
  two new contract sections for `put` (drain a length-less stream) and `get`
  (honour `options.range`).
- keep-generated-files: repoint `#blobstore` at store-reference, and clear the
  em dashes the merged content brought in.
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

19 package(s) bumped directly, 32 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-angular 0.3.1 → 1.0.0 Changeset
@tanstack/ai-durable-stream 0.0.0 → 1.0.0 Changeset
@tanstack/ai-memory 0.0.0 → 1.0.0 Changeset
@tanstack/ai-openai 0.17.1 → 1.0.0 Changeset
@tanstack/ai-openrouter 0.15.10 → 1.0.0 Changeset
@tanstack/ai-persistence 0.0.0 → 1.0.0 Changeset
@tanstack/ai-preact 0.11.1 → 1.0.0 Changeset
@tanstack/ai-react 0.18.1 → 1.0.0 Changeset
@tanstack/ai-sandbox 0.2.4 → 1.0.0 Changeset
@tanstack/ai-solid 0.15.1 → 1.0.0 Changeset
@tanstack/ai-svelte 0.15.1 → 1.0.0 Changeset
@tanstack/ai-vue 0.15.1 → 1.0.0 Changeset
@tanstack/openai-base 0.9.9 → 1.0.0 Changeset
@tanstack/ai-acp 0.2.3 → 1.0.0 Dependent
@tanstack/ai-anthropic 0.16.3 → 1.0.0 Dependent
@tanstack/ai-bedrock 0.1.4 → 1.0.0 Dependent
@tanstack/ai-claude-code 0.2.3 → 1.0.0 Dependent
@tanstack/ai-code-mode 0.3.8 → 1.0.0 Dependent
@tanstack/ai-code-mode-skills 0.3.11 → 1.0.0 Dependent
@tanstack/ai-codex 0.2.3 → 1.0.0 Dependent
@tanstack/ai-elevenlabs 0.2.34 → 1.0.0 Dependent
@tanstack/ai-fal 0.9.12 → 1.0.0 Dependent
@tanstack/ai-gemini 0.20.1 → 1.0.0 Dependent
@tanstack/ai-grok 0.14.9 → 1.0.0 Dependent
@tanstack/ai-grok-build 0.2.3 → 1.0.0 Dependent
@tanstack/ai-groq 0.5.3 → 1.0.0 Dependent
@tanstack/ai-isolate-node 0.1.47 → 1.0.0 Dependent
@tanstack/ai-isolate-quickjs 0.1.47 → 1.0.0 Dependent
@tanstack/ai-mistral 0.2.3 → 1.0.0 Dependent
@tanstack/ai-ollama 0.8.16 → 1.0.0 Dependent
@tanstack/ai-opencode 0.2.3 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.8.15 → 1.0.0 Dependent
@tanstack/ai-sandbox-cloudflare 0.2.4 → 1.0.0 Dependent
@tanstack/ai-sandbox-daytona 0.2.0 → 1.0.0 Dependent
@tanstack/ai-sandbox-docker 0.2.0 → 1.0.0 Dependent
@tanstack/ai-sandbox-local-process 0.2.0 → 1.0.0 Dependent
@tanstack/ai-sandbox-sprites 0.2.1 → 1.0.0 Dependent
@tanstack/ai-sandbox-vercel 0.2.0 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.7.14 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai 0.42.0 → 0.43.0 Changeset
@tanstack/ai-client 0.22.1 → 0.23.0 Changeset
@tanstack/ai-devtools-core 0.4.24 → 0.5.0 Changeset
@tanstack/ai-event-client 0.6.8 → 0.7.0 Changeset
@tanstack/ai-utils 0.3.1 → 0.4.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-mcp 0.2.5 → 0.2.6 Changeset
@tanstack/ai-isolate-cloudflare 0.2.38 → 0.2.39 Dependent
@tanstack/ai-vue-ui 0.2.34 → 0.2.35 Dependent
@tanstack/preact-ai-devtools 0.1.67 → 0.1.68 Dependent
@tanstack/react-ai-devtools 0.2.67 → 0.2.68 Dependent
@tanstack/solid-ai-devtools 0.2.67 → 0.2.68 Dependent
ag-ui 0.0.2 → 0.0.3 Dependent

@nx-cloud

nx-cloud Bot commented Jul 31, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit e5537f6

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 3s View ↗

☁️ Nx Cloud last updated this comment at 2026-07-31 11:42:47 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jul 31, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@1034

@tanstack/ai-acp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-acp@1034

@tanstack/ai-angular

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-angular@1034

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@1034

@tanstack/ai-bedrock

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-bedrock@1034

@tanstack/ai-claude-code

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-claude-code@1034

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@1034

@tanstack/ai-code-mode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode@1034

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode-skills@1034

@tanstack/ai-codex

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-codex@1034

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@1034

@tanstack/ai-durable-stream

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-durable-stream@1034

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-elevenlabs@1034

@tanstack/ai-event-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-event-client@1034

@tanstack/ai-fal

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-fal@1034

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@1034

@tanstack/ai-grok

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok@1034

@tanstack/ai-grok-build

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok-build@1034

@tanstack/ai-groq

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-groq@1034

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-cloudflare@1034

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-node@1034

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-quickjs@1034

@tanstack/ai-mcp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mcp@1034

@tanstack/ai-memory

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-memory@1034

@tanstack/ai-mistral

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mistral@1034

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@1034

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@1034

@tanstack/ai-opencode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-opencode@1034

@tanstack/ai-openrouter

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openrouter@1034

@tanstack/ai-persistence

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence@1034

@tanstack/ai-preact

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact@1034

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@1034

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@1034

@tanstack/ai-sandbox

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox@1034

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-cloudflare@1034

@tanstack/ai-sandbox-daytona

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-daytona@1034

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-docker@1034

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-local-process@1034

@tanstack/ai-sandbox-sprites

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-sprites@1034

@tanstack/ai-sandbox-vercel

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-vercel@1034

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@1034

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@1034

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@1034

@tanstack/ai-utils

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-utils@1034

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@1034

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@1034

@tanstack/openai-base

npm i https://pkg.pr.new/TanStack/ai/@tanstack/openai-base@1034

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/preact-ai-devtools@1034

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@1034

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@1034

commit: c2f02d8

Post-merge pass with the docs skill over the file that conflicted:

- collapse the double blank lines the split left before three headings
- `build-your-own-adapter` no longer holds a walkthrough, so stop saying "type
  this page out" and "all four stores" on a page that presents seven
- rewrap two paragraphs the ported edits ran long

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ai-persistence/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md (1)

581-583: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Add the ownership check before serving bytes.

The route accepts a caller-controlled artifactId and performs no session-derived threadId or runId check. A caller who learns or guesses an artifact ID can read another user's media. Add the authorization check and return 404 for unauthorized records before calling retrieveBlob.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/ai-persistence/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md`
around lines 581 - 583, Update the artifact-serving flow around retrieveArtifact
to derive the authorized threadId or runId from the current session and verify
the retrieved record belongs to it before calling retrieveBlob. Return 404 for
missing or unauthorized records, and never serve bytes for an artifact that
fails this ownership check.
♻️ Duplicate comments (1)
packages/ai-persistence/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md (1)

559-560: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Describe only the helper that resolves the blob key.

retrieveArtifact returns the ArtifactRecord; retrieveBlob resolves the stored blob key. Change “Both” to “retrieveBlob”. This repeats the previous review finding.

#!/bin/bash
set -euo pipefail
rg -n -C 8 'retrieveArtifact|retrieveBlob|resolveArtifactBlobKey' packages/ai-persistence/src/retrieve.ts
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/ai-persistence/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md`
around lines 559 - 560, Update the referenced wording to describe only
retrieveBlob as resolving the stored blob key; do not include retrieveArtifact
or “Both,” since retrieveArtifact returns the ArtifactRecord.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/persistence/store-reference.md`:
- Around line 309-315: Update the documented blob persistence and get flow to
persist the actual drained byte count for byte-backed records, rather than using
expectedLength as the object size. Require the stored size before calling
resolveBlobRange for ranged reads, and ensure the example does not evaluate
range handling until that persisted size is available.

---

Outside diff comments:
In
`@packages/ai-persistence/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md`:
- Around line 581-583: Update the artifact-serving flow around retrieveArtifact
to derive the authorized threadId or runId from the current session and verify
the retrieved record belongs to it before calling retrieveBlob. Return 404 for
missing or unauthorized records, and never serve bytes for an artifact that
fails this ownership check.

---

Duplicate comments:
In
`@packages/ai-persistence/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md`:
- Around line 559-560: Update the referenced wording to describe only
retrieveBlob as resolving the stored blob key; do not include retrieveArtifact
or “Both,” since retrieveArtifact returns the ArtifactRecord.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9fbadcb3-ce80-40c2-b6c6-f3eb992f45a4

📥 Commits

Reviewing files that changed from the base of the PR and between 76aff62 and e5537f6.

📒 Files selected for processing (7)
  • docs/persistence/build-your-own-adapter.md
  • docs/persistence/build-your-own-generation-adapter.md
  • docs/persistence/keep-generated-files.md
  • docs/persistence/store-reference.md
  • packages/ai-persistence/skills/ai-persistence/build-cloudflare-artifact-store/SKILL.md
  • packages/ai-persistence/src/index.ts
  • packages/ai-persistence/src/retrieve.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/ai-persistence/src/index.ts
  • docs/persistence/build-your-own-generation-adapter.md
  • packages/ai-persistence/src/retrieve.ts
  • docs/persistence/build-your-own-adapter.md

Comment on lines +309 to +315
And one for `get`: honour `options.range` by returning **only that slice**.
`size` keeps reporting the whole object, and the returned `range` reports what
you actually served. Together they are the `206` response a media player's
seeking depends on. `resolveBlobRange(size, range)` does the clamping (a
`length` past the end is legal and clamps; an `offset` past the end throws,
because a serve route should have answered `416` from `record.size` first):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C 8 'interface BlobRecord|size\?: number|resolveBlobRange|expectedLength|BlobStore' packages/ai-persistence docs/persistence

Repository: TanStack/ai

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docs/persistence/store-reference.md relevant lines =="
sed -n '280,340p' docs/persistence/store-reference.md

echo
echo "== docs/persistence/store-reference.md lines 318-330 =="
sed -n '318,330p' docs/persistence/store-reference.md

echo
echo "== packages/ai-persistence/src/blob-range.ts =="
cat -n packages/ai-persistence/src/blob-range.ts

echo
echo "== packages/ai-persistence/src/memory.ts put/get relevant lines =="
sed -n '330,425p' packages/ai-persistence/src/memory.ts

echo
echo "== packages/ai-persistence/src/testkit/conformance.ts put/expectedLength tests =="
sed -n '760,798p' packages/ai-persistence/src/testkit/conformance.ts

echo
echo "== local references to size undefined in blob helpers/tests =="
rg -n "blobObject\\(|mapBlobRecord\\(|resolveBlobRange\\(|size\\?:" packages/ai-persistence/docs packages/ai-persistence/src -g '.*' | head -n 120

Repository: TanStack/ai

Length of output: 13968


Persist and require the stored byte count for ranged blobs.

resolveBlobRange(row.size, options.range) passes the stored row size into BlobRecord.size, and the example can evaluate that path before the stored bytes are known. Persist the real drained byte count for byte-backed records, require it for range-capable reads, and do not store expectedLength as the object size.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/persistence/store-reference.md` around lines 309 - 315, Update the
documented blob persistence and get flow to persist the actual drained byte
count for byte-backed records, rather than using expectedLength as the object
size. Require the stored size before calling resolveBlobRange for ranged reads,
and ensure the example does not evaluate range handling until that persisted
size is available.

@AlemTuzlak
AlemTuzlak merged commit 826cfed into main Jul 31, 2026
9 of 10 checks passed
@AlemTuzlak
AlemTuzlak deleted the refactor/trim-persistence-public-surface branch July 31, 2026 11:56
tombeckenham added a commit that referenced this pull request Jul 31, 2026
Reconciles #1034's persistence docs split and API trim with this branch's
durable-run additions:

- build-your-own-adapter.md: took main's hub; re-homed this branch's
  skipMethods conformance content into its conformance section.
- build-your-own-chat-adapter.md: re-homed the durable-run walkthrough
  changes (runs-table columns, 'aborted' status, RunError two-column
  mapping, presence-keyed update branches, listByThread/listReclaimable).
- store-reference.md: re-homed the RunStore contract rewrite; widened
  GenerationRunStatus to match GenerationRunStatus = RunStatus.
- chat-persistence.md: kept this branch's abort/detach semantics,
  restyled to main's no-em-dash pass.
- Kept the isTerminalRunStatus / TerminalRunStatus re-exports in
  @tanstack/ai-persistence: the store reference teaches them to backend
  authors, which meets #1034's "what apps call" bar. Main's removals
  (artifactBlobKey, createInterruptController) stand.
- Retargeted sandbox-doc and adapter-skill references from the
  dismantled hub page to store-reference / build-your-own-chat-adapter.

Verified: pnpm test:pr green (70 projects), doc links clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant