Skip to content

docs: update AI SDK comparison for persistence, durability, and adapters - #1097

Merged
AlemTuzlak merged 2 commits into
mainfrom
docs/comparison-persistence-durability-adapters
Aug 12, 2026
Merged

docs: update AI SDK comparison for persistence, durability, and adapters#1097
AlemTuzlak merged 2 commits into
mainfrom
docs/comparison-persistence-durability-adapters

Conversation

@AlemTuzlak

@AlemTuzlak AlemTuzlak commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Changes

Refresh the Vercel AI SDK comparison page for AI SDK v7.

  • Add rows and sections for chat persistence (withPersistence, reconstructChat), generation persistence, resumable streams (memoryStream / durableStream), and durable sandbox runs (runs + durability).
  • Update Code Mode to 5 isolate drivers vs Vercel experimental QuickJS-only @ai-sdk/code-mode.
  • Update adapter counts: 15 official LLM adapters, 5 harness adapters, official Angular (@tanstack/ai-angular), plus community adapters.
  • Add server + client snippets for persistence and stream resume. Update the choose lists and "Where Vercel Excels".

Docs-only. No changeset.

Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.
  • I ran pnpm test:docs (link check). No broken links.

Release Impact

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

Summary by CodeRabbit

  • Documentation

    • Updated the Vercel AI SDK v7 comparison with expanded provider and framework coverage.
    • Added comparisons for chat persistence, resumable streams, durable runs, code-mode workflows, sandbox environments, and realtime voice.
    • Included new examples and refreshed guidance for selecting between platforms and capabilities.
  • Chores

    • Updated supported Vercel AI SDK package versions to the latest major releases.

Refresh the Vercel AI SDK comparison for v7. Cover chat persistence, resumable streams, durable sandbox runs, Code Mode drivers, harness adapters, and the official LLM provider list.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The comparison updates Vercel AI SDK coverage to v7. It adds documentation for persistence, resumable streams, durable sandbox runs, Code Mode, coding-agent sandboxes, adapters, frameworks, realtime voice, and package version configuration.

Changes

Vercel AI SDK comparison

Layer / File(s) Summary
Comparison scope and integrations
docs/comparison/vercel-ai-sdk.md, kiira.config.ts
Updates the page metadata, Vercel AI SDK version, framework and provider coverage, adapter listings, Code Mode references, realtime voice comparison, and configured package versions.
Persistence and durable data flows
docs/comparison/vercel-ai-sdk.md
Documents transcript persistence, resumable streams, durable sandbox runs, replay, recovery, and persistence implementation examples.
Sandbox architecture and selection guidance
docs/comparison/vercel-ai-sdk.md
Adds Code Mode, coding-agent sandbox architecture, harness comparisons, ACP support, and revised capability selection criteria.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the documentation update and names the main comparison topics: persistence, durability, and adapters.
Description check ✅ Passed The description includes the required change summary, checklist, and release impact sections, with clear docs-only scope and testing details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/comparison-persistence-durability-adapters

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@nx-cloud

nx-cloud Bot commented Aug 12, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 6143335

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

☁️ Nx Cloud last updated this comment at 2026-08-12 16:32:09 UTC

@pkg-pr-new

pkg-pr-new Bot commented Aug 12, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

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

@tanstack/ai-acp

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

@tanstack/ai-angular

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

@tanstack/ai-anthropic

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

@tanstack/ai-bedrock

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

@tanstack/ai-byteplus

npm i https://pkg.pr.new/@tanstack/ai-byteplus@1097

@tanstack/ai-claude-code

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

@tanstack/ai-client

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

@tanstack/ai-code-mode

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

@tanstack/ai-code-mode-skills

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

@tanstack/ai-codex

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

@tanstack/ai-cohere

npm i https://pkg.pr.new/@tanstack/ai-cohere@1097

@tanstack/ai-devtools-core

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

@tanstack/ai-durable-stream

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

@tanstack/ai-elevenlabs

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

@tanstack/ai-event-client

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

@tanstack/ai-fal

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

@tanstack/ai-gemini

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

@tanstack/ai-grok

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

@tanstack/ai-grok-build

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

@tanstack/ai-groq

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

@tanstack/ai-isolate-cloudflare

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

@tanstack/ai-isolate-daytona

npm i https://pkg.pr.new/@tanstack/ai-isolate-daytona@1097

@tanstack/ai-isolate-node

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

@tanstack/ai-isolate-quickjs

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

@tanstack/ai-isolate-quickjs-bun

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs-bun@1097

@tanstack/ai-mcp

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

@tanstack/ai-memory

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

@tanstack/ai-mistral

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

@tanstack/ai-ollama

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

@tanstack/ai-openai

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

@tanstack/ai-opencode

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

@tanstack/ai-openrouter

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

@tanstack/ai-persistence

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

@tanstack/ai-preact

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

@tanstack/ai-react

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

@tanstack/ai-react-ui

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

@tanstack/ai-sandbox

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

@tanstack/ai-sandbox-cloudflare

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

@tanstack/ai-sandbox-daytona

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

@tanstack/ai-sandbox-docker

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

@tanstack/ai-sandbox-local-process

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

@tanstack/ai-sandbox-sprites

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

@tanstack/ai-sandbox-vercel

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

@tanstack/ai-solid

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

@tanstack/ai-solid-ui

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

@tanstack/ai-svelte

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

@tanstack/ai-utils

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

@tanstack/ai-vercel-gateway

npm i https://pkg.pr.new/@tanstack/ai-vercel-gateway@1097

@tanstack/ai-vue

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

@tanstack/ai-vue-ui

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

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@1097

@tanstack/preact-ai-devtools

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

@tanstack/react-ai-devtools

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

@tanstack/solid-ai-devtools

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

commit: 6143335

@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: 5

🧹 Nitpick comments (1)
docs/comparison/vercel-ai-sdk.md (1)

336-336: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Update the edited OpenAI model IDs.

Use gpt-5.6 at lines 336, 389, and 845. Use a supported openai/gpt-5.6-* variant at line 876. Keep claude-opus-4-8 at line 436.

🤖 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/comparison/vercel-ai-sdk.md` at line 336, Update the OpenAI model
identifiers in the documented examples: change the models at the entries
corresponding to lines 336, 389, and 845 to gpt-5.6, and replace the line 876
value with a supported openai/gpt-5.6-* variant. Leave claude-opus-4-8
unchanged.

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/comparison/vercel-ai-sdk.md`:
- Around line 865-890: Extend the Vercel AI SDK example after the POST route
with a client snippet using useChat and the appropriate transport configuration.
Pass the chat ID as a request parameter or body value so it reaches the existing
route, matching the persistence flow shown by the TanStack AI example.
- Around line 875-888: Update the stream handling around result.stream and
createUIMessageStreamResponse to start a server-side drain before returning the
response, ensuring the stream continues to completion after client disconnects
so onEnd still invokes saveChat. Preserve the existing UI response stream and
message-saving behavior.
- Around line 333-341: Update the POST handler and related persistence flows to
derive the authenticated user from the server session, authorize ownership of
client-provided threadId and runId before calling chat, reconstructChat,
resumeServerSentEventsResponse, or sandbox takeover, and reject unauthorized
requests. Clearly mark literal identifiers such as support-chat as demo-only.
- Around line 871-877: Update the POST handler to validate the combined loaded
messages and new message with validateUIMessages before calling
convertToModelMessages or streamText. Handle validation failures immediately,
and only pass the validated messages into model conversion while preserving the
existing chat-loading flow.
- Around line 412-414: Update the withSandbox durability documentation to
clarify that durable cross-host runs require durableStream with an external
stream backend plus shared run and lock stores; otherwise, explicitly label the
memoryStream example as single-process development only.

---

Nitpick comments:
In `@docs/comparison/vercel-ai-sdk.md`:
- Line 336: Update the OpenAI model identifiers in the documented examples:
change the models at the entries corresponding to lines 336, 389, and 845 to
gpt-5.6, and replace the line 876 value with a supported openai/gpt-5.6-*
variant. Leave claude-opus-4-8 unchanged.
🪄 Autofix

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: 2d138e4a-5b58-4a1c-9a47-f7375b6a237c

📥 Commits

Reviewing files that changed from the base of the PR and between 25f14ad and 43dfa54.

📒 Files selected for processing (1)
  • docs/comparison/vercel-ai-sdk.md

Comment on lines +333 to +341
export async function POST(request: Request) {
const params = await chatParamsFromRequest(request)
const stream = chat({
adapter: openaiText('gpt-5.5'),
messages: params.messages,
threadId: params.threadId,
runId: params.runId,
middleware: [withPersistence(persistence)],
})

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- target file ---'
sed -n '300,470p;820,890p' docs/comparison/vercel-ai-sdk.md
printf '%s\n' '--- relevant identifiers across docs ---'
rg -n -C 3 'withPersistence|reconstructChat|resumeServerSentEventsResponse|threadId|runId|sandbox|takeover|support-chat' docs/comparison/vercel-ai-sdk.md
printf '%s\n' '--- API definitions and usage ---'
rg -n -C 4 'function reconstructChat|class ReconstructChat|interface ReconstructChatOptions|resumeServerSentEventsResponse|withPersistence' packages docs | head -n 300

Repository: TanStack/ai

Length of output: 50369


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- persistence API files ---'
fd -t f . packages/ai-persistence packages/ai packages/ai-durable-stream packages/ai-sandbox | head -n 120
printf '%s\n' '--- persistence symbols outside changelogs ---'
rg -n -g '!**/CHANGELOG.md' -C 5 'reconstructChat|withPersistence|authorize|resumeServerSentEventsResponse|takeover|threadId|runId' packages/ai-persistence packages/ai packages/ai-durable-stream packages/ai-sandbox docs/persistence docs/resumable-streams docs/sandbox | head -n 500
printf '%s\n' '--- persistence overview ---'
sed -n '1,180p' docs/persistence/overview.md
printf '%s\n' '--- target document around all cited ranges ---'
sed -n '330,460p;830,885p' docs/comparison/vercel-ai-sdk.md

Repository: TanStack/ai

Length of output: 50369


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- persistence package source ---'
fd -t f . packages/ai-persistence/src packages/ai-durable-stream/src packages/ai-sandbox/src
printf '%s\n' '--- authorization and helper signatures ---'
rg -n -g '!**/CHANGELOG.md' -g '*.ts' -C 8 'reconstructChat|authorize|resumeServerSentEventsResponse|sandboxRunDriver|takeover' packages/ai-persistence/src packages/ai/src packages/ai-durable-stream/src packages/ai-sandbox/src
printf '%s\n' '--- persistence overview authorization guidance ---'
rg -n -C 8 'authorize|session|ownership|threadId|reconstructChat' docs/persistence/overview.md
printf '%s\n' '--- sandbox takeover documentation ---'
rg -n -C 8 'authorize|ownership|session|threadId|runId|takeover|GET' docs/sandbox docs/resumable-streams/overview.md

Repository: TanStack/ai

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- exact persistence source files ---'
fd -t f . packages/ai-persistence/src | sort
printf '%s\n' '--- exact helper definitions ---'
rg -l -g '*.ts' 'reconstructChat|authorize' packages/ai-persistence/src packages/ai/src packages/ai-durable-stream/src packages/ai-sandbox/src | while read -r f; do
  echo "### $f"
  rg -n -C 12 'reconstructChat|authorize' "$f"
done
printf '%s\n' '--- resumable response definitions ---'
rg -l -g '*.ts' 'resumeServerSentEventsResponse' packages/ai/src packages/ai-durable-stream/src | while read -r f; do
  echo "### $f"
  rg -n -C 16 'resumeServerSentEventsResponse' "$f"
done
printf '%s\n' '--- takeover route examples and guidance ---'
rg -n -C 12 -g '*.md' -g '*.mdx' 'sandboxRunDriver|resumeServerSentEventsResponse|authorize|ownership|session|threadId|runId' docs/sandbox/takeover.md docs/sandbox/durable-runs.md docs/resumable-streams/overview.md docs/resumable-streams/advanced.md 2>/dev/null

Repository: TanStack/ai

Length of output: 50369


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path

target = Path("docs/comparison/vercel-ai-sdk.md").read_text()
reconstruct = Path("packages/ai-persistence/src/reconstruct.ts").read_text()
response = Path("packages/ai/src/stream-to-response.ts").read_text()
overview = Path("docs/persistence/overview.md").read_text()

checks = {
    "comparison_has_client_thread_id": "threadId: 'support-chat'" in target,
    "comparison_has_server_client_ids": all(x in target for x in [
        "threadId: params.threadId", "runId: params.runId"
    ]),
    "comparison_has_reconstruct_authorize_example": "authorize:" in target,
    "reconstruct_exposes_authorize": "authorize?:" in reconstruct,
    "reconstruct_authorizes_before_store_load": reconstruct.index("options?.authorize") < reconstruct.index("messageStore.loadThread"),
    "resume_helper_has_no_authorize_option": "authorize?:" not in response[response.index("export function resumeServerSentEventsResponse"):response.index("export function resumeHttpResponse")],
    "overview_warns_without_authorization": "anyone who guesses a thread id gets the whole transcript" in overview,
}
for name, value in checks.items():
    print(f"{name}={value}")
if not all(checks.values()):
    raise SystemExit("unexpected documentation/API shape")
PY

Repository: TanStack/ai

Length of output: 476


Authorize client-provided thread and run identifiers.

Derive identity from the server session and check ownership before invoking chat with persistence, reconstructChat, resumeServerSentEventsResponse, or sandbox takeover. Otherwise, a guessed identifier can expose another user's transcript or stream, or control another user's run. Mark literal IDs such as support-chat as demo-only.

🤖 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/comparison/vercel-ai-sdk.md` around lines 333 - 341, Update the POST
handler and related persistence flows to derive the authenticated user from the
server session, authorize ownership of client-provided threadId and runId before
calling chat, reconstructChat, resumeServerSentEventsResponse, or sandbox
takeover, and reject unauthorized requests. Clearly mark literal identifiers
such as support-chat as demo-only.

Source: Learnings

Comment on lines +412 to +414
Without durability, TanStack AI destroys the sandbox on disconnect. That stops a runaway bill. With durability, the agent detaches, writes a journal at `/tmp/tanstack-runs/<runId>.ndjson`, and a later `GET` can take the run over.

Turn it on with both `runs` and `durability` on `withSandbox`. Passing only one leaves the default destroy-on-disconnect behavior.

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 -eu
printf '%s\n' '--- target excerpt ---'
sed -n '390,465p' docs/comparison/vercel-ai-sdk.md
printf '%s\n' '--- stream API references ---'
rg -n --glob '!node_modules' 'memoryStream|durableStream|withSandbox|journal|tanstack-runs' .
printf '%s\n' '--- candidate TanStack AI files ---'
git ls-files | rg '(^|/)(model-meta\.ts|.*sandbox.*|.*stream.*|.*durability.*)'

Repository: TanStack/ai

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- target excerpt ---'
sed -n '400,455p' docs/comparison/vercel-ai-sdk.md
printf '%s\n' '--- focused implementation references ---'
rg -n --glob '!**/CHANGELOG.md' --glob '!**/*.test.*' --glob '!node_modules/**' \
  'function memoryStream|const memoryStream|memoryStream\s*=|function durableStream|const durableStream|durableStream\s*=|withSandbox' \
  packages docs | head -n 240
printf '%s\n' '--- package files ---'
git ls-files packages | rg '(^|/)(stream|durability|sandbox|run|journal)[^/]*\.(ts|tsx|md)$' | head -n 240

Repository: TanStack/ai

Length of output: 32691


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- memoryStream implementation ---'
sed -n '250,390p' packages/ai/src/stream-durability.ts
printf '%s\n' '--- durableStream implementation ---'
sed -n '430,535p' packages/ai-durable-stream/src/durable-stream.ts
printf '%s\n' '--- stream response durability path ---'
sed -n '330,455p' packages/ai/src/stream-to-response.ts
printf '%s\n' '--- official durable-run guidance in repository ---'
sed -n '1,115p' docs/sandbox/durable-runs.md
sed -n '95,190p' docs/sandbox/takeover.md
printf '%s\n' '--- journal guidance ---'
sed -n '35,115p' docs/sandbox/journal.md

Repository: TanStack/ai

Length of output: 27284


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- comparison context ---'
sed -n '330,465p' docs/comparison/vercel-ai-sdk.md
printf '%s\n' '--- takeover attach route ---'
rg -n -A90 -B20 'GET|attach|durableStream\(' docs/sandbox/takeover.md | head -n 260
printf '%s\n' '--- stream durability type comments ---'
sed -n '1,120p' packages/ai/src/stream-durability.ts
sed -n '535,620p' packages/ai-durable-stream/src/durable-stream.ts
printf '%s\n' '--- memory backend caveats across docs ---'
rg -n -A8 -B8 'process-local|single-process|development stand-in|real deployment|multi-process|different host' \
  docs/sandbox docs/comparison/vercel-ai-sdk.md packages/ai/src/stream-durability.ts packages/ai-durable-stream/src/durable-stream.ts

Repository: TanStack/ai

Length of output: 31052


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path

comparison = Path("docs/comparison/vercel-ai-sdk.md").read_text()
implementation = Path("packages/ai/src/stream-durability.ts").read_text()

target = comparison[comparison.index("### Durable Sandbox Runs"):comparison.index("### Connection Adapters")]
assert "The next request can land on a different host." in target
assert "const adapter = memoryStream(request)" in target
assert "withSandbox(sandbox, {" in target
assert "durability: { adapter }" in target

assert "const memoryLogs = new Map<string, MemoryLog>()" in implementation
assert "process-global map" in implementation
assert "single-process deployments only" in implementation

print("durable sandbox sample claims cross-host takeover while wiring memoryStream")
print("memoryStream stores logs in a process-global Map and is documented for single-process deployments only")
PY

Repository: TanStack/ai

Length of output: 328


Use durableStream for cross-host durable runs. memoryStream stores the delivery log in a process-local map, so a GET on another host cannot replay or take over the run. Use an external stream backend and shared run and lock stores, or label this example as single-process development only.

🤖 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/comparison/vercel-ai-sdk.md` around lines 412 - 414, Update the
withSandbox durability documentation to clarify that durable cross-host runs
require durableStream with an external stream backend plus shared run and lock
stores; otherwise, explicitly label the memoryStream example as single-process
development only.

Comment on lines +865 to +890
**Vercel AI SDK** - You own `saveChat` / `loadChat` and call them from `onEnd`:

```ts
import { convertToModelMessages, createUIMessageStreamResponse, streamText, toUIMessageStream } from 'ai'
import { loadChat, saveChat } from './chat-store'

export async function POST(req: Request) {
const { message, id } = await req.json()
const messages = [...(await loadChat(id)), message]

const result = streamText({
model: 'openai/gpt-5.5',
messages: await convertToModelMessages(messages),
})

return createUIMessageStreamResponse({
stream: toUIMessageStream({
stream: result.stream,
originalMessages: messages,
onEnd: ({ messages: next }) => {
saveChat({ chatId: id, messages: next })
},
}),
})
}
```

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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

file="docs/comparison/vercel-ai-sdk.md"

printf '%s\n' '--- target section ---'
sed -n '800,930p' "$file"

printf '%s\n' '--- section headings ---'
rg -n '^#{1,6} ' "$file" | sed -n '1,140p'

printf '%s\n' '--- client/server persistence terms ---'
rg -n -C 3 'useChat|DefaultChatTransport|chatId|saveChat|loadChat|persistence|reload|client|server' "$file" | sed -n '1,240p'

Repository: TanStack/ai

Length of output: 20674


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path
import re

path = Path("docs/comparison/vercel-ai-sdk.md")
text = path.read_text()

section = text.split("### Persistence\n", 1)[1].split("### Agent Loop Control\n", 1)[0]
blocks = re.findall(r"```(?:tsx?|jsx?)\n(.*?)```", section, re.S)

print(f"persistence_code_blocks={len(blocks)}")
for i, block in enumerate(blocks, 1):
    side = "TanStack" if "fetchServerSentEvents" in block or "withPersistence" in block else "Vercel" if "createUIMessageStreamResponse" in block or "toUIMessageStream" in block else "other"
    print(f"block_{i}_side={side}")
    print(f"block_{i}_has_useChat={bool(re.search(r'\\buseChat\\b', block))}")
    print(f"block_{i}_has_chat_id_transport={bool(re.search(r'(chatId|id).*transport|transport.*(chatId|id)', block, re.S))}")

vercel = next(
    block for block in blocks
    if "createUIMessageStreamResponse" in block and "toUIMessageStream" in block
)
print(f"vercel_has_useChat={bool(re.search(r'\\buseChat\\b', vercel))}")
print(f"vercel_has_transport={bool(re.search(r'\\btransport\\b', vercel))}")
print(f"vercel_has_id_in_route={bool(re.search(r'\\bid\\b', vercel))}")
PY

Repository: TanStack/ai

Length of output: 398


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path
import re

path = Path("docs/comparison/vercel-ai-sdk.md")
text = path.read_text()
section = text.split("### Persistence\n", 1)[1].split("### Agent Loop Control\n", 1)[0]
blocks = re.findall(r"```(?:tsx?|jsx?)\n(.*?)```", section, re.S)

use_chat = re.compile(r"\buseChat\b")
transport = re.compile(r"\btransport\b")
chat_id = re.compile(r"\b(?:chatId|id|threadId)\b")

print(f"persistence_code_blocks={len(blocks)}")
for i, block in enumerate(blocks, 1):
    side = (
        "TanStack"
        if "fetchServerSentEvents" in block or "withPersistence" in block
        else "Vercel"
        if "createUIMessageStreamResponse" in block or "toUIMessageStream" in block
        else "other"
    )
    print(f"block_{i}_side={side}")
    print(f"block_{i}_has_useChat={bool(use_chat.search(block))}")
    print(f"block_{i}_has_transport={bool(transport.search(block))}")
    print(f"block_{i}_has_chat_identifier={bool(chat_id.search(block))}")

vercel = next(
    block for block in blocks
    if "createUIMessageStreamResponse" in block and "toUIMessageStream" in block
)
print(f"vercel_has_useChat={bool(use_chat.search(vercel))}")
print(f"vercel_has_transport={bool(transport.search(vercel))}")
print(f"vercel_has_chat_identifier={bool(chat_id.search(vercel))}")
PY

Repository: TanStack/ai

Length of output: 2230


Add the client half to the Vercel persistence example.

The section includes server and client snippets for TanStack AI, but only a server route for Vercel AI SDK. Add a matching useChat and transport configuration that passes the chat ID to the route.

🤖 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/comparison/vercel-ai-sdk.md` around lines 865 - 890, Extend the Vercel
AI SDK example after the POST route with a client snippet using useChat and the
appropriate transport configuration. Pass the chat ID as a request parameter or
body value so it reaches the existing route, matching the persistence flow shown
by the TanStack AI example.

Source: Coding guidelines

Comment on lines +871 to +877
export async function POST(req: Request) {
const { message, id } = await req.json()
const messages = [...(await loadChat(id)), message]

const result = streamText({
model: 'openai/gpt-5.5',
messages: await convertToModelMessages(messages),

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- target excerpt ---'
sed -n '835,900p' docs/comparison/vercel-ai-sdk.md
printf '%s\n' '--- validation API references ---'
rg -n "validateUIMessages|convertToModelMessages|loadChat|streamText" docs packages examples --glob '!node_modules' 2>/dev/null | head -200
printf '%s\n' '--- package and source candidates ---'
git ls-files | rg '(^|/)(package.json|model-meta\.ts|.*message.*|.*chat.*)' | head -200

Repository: TanStack/ai

Length of output: 20702


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- target file structure ---'
ast-grep outline docs/comparison/vercel-ai-sdk.md 2>/dev/null || true
printf '%s\n' '--- nearby comparison content ---'
sed -n '760,930p' docs/comparison/vercel-ai-sdk.md
printf '%s\n' '--- repository metadata ---'
find . -maxdepth 2 -name package.json -o -name pnpm-lock.yaml | sort | head -50

Repository: TanStack/ai

Length of output: 7150


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
p = Path("docs/comparison/vercel-ai-sdk.md")
text = p.read_text()
for needle in ("validateUIMessages", "convertToModelMessages", "gpt-5.5"):
    print(f"--- occurrences of {needle} ---")
    for i, line in enumerate(text.splitlines(), 1):
        if needle in line:
            print(f"{i}: {line}")
PY

Repository: TanStack/ai

Length of output: 1593


🌐 Web query:

AI SDK 6 chat persistence validateUIMessages convertToModelMessages official documentation

💡 Result:

In the AI SDK, validateUIMessages and convertToModelMessages are essential functions for managing chatbot message persistence and integrating UI messages with core AI model functions [1][2]. validateUIMessages validateUIMessages is an asynchronous function used to validate UI messages against specified schemas for metadata, data parts, and tools [3][4]. It ensures data integrity and type safety by checking messages before they are processed or rendered [3]. Usage: It is typically used on the server when loading chat history from a database, particularly if those messages contain tool calls, custom metadata, or data parts [1][5]. import { validateUIMessages } from 'ai'; const validatedMessages = await validateUIMessages({ messages, tools, dataSchemas, metadataSchema, }); A related function, safeValidateUIMessages, performs the same validation but returns a result object (with a success boolean) instead of throwing an error [6][7]. convertToModelMessages convertToModelMessages transforms an array of UI messages (typically from the useChat hook) into an array of ModelMessage objects, which are compatible with core AI functions such as streamText and generateText [2][8]. Usage: It is commonly used after validation to prepare message history for the AI model [1][5]. It supports optional configurations to handle incomplete tool calls and custom data part conversion [2][9]. import { convertToModelMessages } from 'ai'; const modelMessages = await convertToModelMessages(validatedMessages, { tools, // Optional: define how to convert custom data parts convertDataPart: (part) => { /*... */ } }); When implementing chatbot persistence, the standard pattern involves loading previous messages, validating them with validateUIMessages, converting them with convertToModelMessages for the AI stream, and finally saving the updated message list (often via an onEnd or onFinish callback) [1][10].

Citations:


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- persistence documentation references ---'
sed -n '330,375p' docs/comparison/vercel-ai-sdk.md
rg -n -C 5 "validateUIMessages|Chat Persistence|message persistence" docs packages examples pnpm-lock.yaml package.json --glob '!node_modules'
printf '%s\n' '--- message type and conversion implementations ---'
rg -n "function (convertToModelMessages|validateUIMessages)|const (convertToModelMessages|validateUIMessages)|validate.*Message|convert.*Message" packages --glob '*.{ts,tsx,js}' | head -200

Repository: TanStack/ai

Length of output: 24929


🌐 Web query:

site:ai-sdk.dev/docs/ai-sdk-ui/chatbot-message-persistence validateUIMessages messages

💡 Result:

The validateUIMessages function is a utility provided by the Vercel AI SDK [1]. It is primarily used to validate messages retrieved from storage—such as a database—before they are processed by a model or sent to the AI SDK's streaming functions [1]. This validation step is essential when messages contain tool calls, custom metadata, or data parts [1]. By using validateUIMessages, developers ensure that the stored messages conform to the current schemas for tools, data parts, and metadata [1]. This prevents errors that might occur if the underlying schema definitions have changed since the messages were originally saved. To use the function, you pass an object containing the messages you wish to validate along with your current tool definitions and relevant schemas [1]: const validatedMessages = await validateUIMessages({ messages, tools, dataPartsSchema, metadataSchema, }); Once validated, these messages can be safely converted to model messages and processed within your application's workflow [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from urllib.request import urlopen
from bs4 import BeautifulSoup
urls = [
    "https://ai-sdk.dev/docs/ai-sdk-ui/chatbot-message-persistence",
    "https://raw.githubusercontent.com/vercel/ai/main/content/docs/04-ai-sdk-ui/03-chatbot-message-persistence.mdx",
]
for url in urls:
    print(f"--- {url} ---")
    try:
        data = urlopen(url, timeout=15).read().decode()
        if url.endswith(".mdx"):
            print(data[:20000])
        else:
            soup = BeautifulSoup(data, "html.parser")
            text = soup.get_text("\n")
            lines = [line.strip() for line in text.splitlines() if line.strip()]
            for i, line in enumerate(lines):
                if "validateUIMessages" in line or "convertToModelMessages" in line:
                    print("\n".join(lines[max(0, i-8):i+18]))
    except Exception as e:
        print(type(e).__name__, e)
PY

Repository: TanStack/ai

Length of output: 265


🏁 Script executed:

#!/bin/bash
set -e
for url in \
  'https://raw.githubusercontent.com/vercel/ai/main/content/docs/04-ai-sdk-ui/03-chatbot-message-persistence.mdx' \
  'https://raw.githubusercontent.com/vercel/ai/main/packages/ai/src/ui/validate-ui-messages.ts' \
  'https://raw.githubusercontent.com/vercel/ai/main/packages/ai/src/ui/convert-to-model-messages.ts'; do
  printf '\n--- %s ---\n' "$url"
  curl -L --fail --silent --show-error "$url" | sed -n '1,260p'
done

Repository: TanStack/ai

Length of output: 28599


Validate messages before model conversion.

Call validateUIMessages on [...previousMessages, message] and handle validation failures before convertToModelMessages and streamText. This protects persisted tool calls, metadata, and data parts from invalid schemas.

🤖 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/comparison/vercel-ai-sdk.md` around lines 871 - 877, Update the POST
handler to validate the combined loaded messages and new message with
validateUIMessages before calling convertToModelMessages or streamText. Handle
validation failures immediately, and only pass the validated messages into model
conversion while preserving the existing chat-loading flow.

Comment on lines +875 to +888
const result = streamText({
model: 'openai/gpt-5.5',
messages: await convertToModelMessages(messages),
})

return createUIMessageStreamResponse({
stream: toUIMessageStream({
stream: result.stream,
originalMessages: messages,
onEnd: ({ messages: next }) => {
saveChat({ chatId: id, messages: next })
},
}),
})

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- target snippet ---'
sed -n '840,910p' docs/comparison/vercel-ai-sdk.md
printf '%s\n' '--- stream API references ---'
rg -n "consumeStream|onEnd|createUIMessageStreamResponse|toUIMessageStream|streamText" . -g '!node_modules' -g '!dist' -g '!build' | head -200
printf '%s\n' '--- package metadata ---'
rg -n '"ai"|"`@ai-sdk/`' package.json pnpm-lock.yaml '**/package.json' 2>/dev/null | head -120

Repository: TanStack/ai

Length of output: 14074


🌐 Web query:

Vercel AI SDK streamText consumeStream client disconnect onEnd createUIMessageStreamResponse official persistence guide

💡 Result:

To handle client disconnects and ensure message persistence in the Vercel AI SDK when using streamText, you must explicitly consume the stream on the backend [1][2]. Core Mechanism for Persistence By default, streamText uses backpressure; if the client disconnects, the stream is aborted, which can lead to broken conversations [1]. To ensure the stream completes and triggers your onEnd logic even if the client leaves, call result.consumeStream on your StreamTextResult before returning the response [1][2]. Example Implementation: import { convertToModelMessages, createUIMessageStreamResponse, streamText, toUIMessageStream } from 'ai'; import { saveChat } from '@util/chat-store'; export async function POST(req: Request) { const { messages, chatId } = await req.json; const result = streamText({ model, messages: await convertToModelMessages(messages), }); // Consume the stream to ensure it runs to completion & triggers onEnd // even when the client response is aborted: result.consumeStream; return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream, originalMessages: messages, onEnd: ({ messages }) => { saveChat({ chatId, messages }); }, }), }); } Important Considerations: 1. Handling Abort Logic: If you are also implementing explicit stream cancellation (e.g., a "Stop" button), note that forwarding req.signal as the abortSignal to streamText will cancel the generation upon ANY client disconnect, including accidental ones [2][3]. If you require resumable streams, you must avoid using req.signal directly in a way that terminates the process on unintentional disconnects; instead, use a dedicated stop endpoint to handle intentional user cancellation [3][4]. 2. Error Handling: Ensure your code handles potential errors from consumeStream correctly. In some environments, if an abort signal is passed, consumeStream may throw a response-aborted error that should be caught to prevent it from bubbling up as a network error [5]. 3. Official Documentation: Refer to the official AI SDK documentation on Chatbot Message Persistence [1] and Advanced: Stopping Streams [2] for the most up-to-date patterns.

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
p = Path("docs/comparison/vercel-ai-sdk.md")
lines = p.read_text().splitlines()
for i in range(850, 901):
    print(f"{i+1}: {lines[i]}")
PY
printf '%s\n' '--- local package/version references ---'
rg -n '"ai"|"`@ai-sdk/`' --glob 'package.json' --glob 'pnpm-lock.yaml' . | head -120 || true
printf '%s\n' '--- local API references ---'
rg -n 'consumeStream|createUIMessageStreamResponse|toUIMessageStream|onEnd' --glob '!node_modules' --glob '!dist' --glob '!build' . | head -200 || true
printf '%s\n' '--- official docs text ---'
curl -L --max-time 20 -sS 'https://ai-sdk.dev/docs/ai-sdk-ui/chatbot-message-persistence' | tr '\n' ' ' | grep -oE '.{0,300}(consumeStream|onFinish|onEnd).{0,500}' | head -20 || true

Repository: TanStack/ai

Length of output: 24380


Drain the stream after client disconnects.

If the client disconnects before result.stream finishes, onEnd may not run and saveChat may not execute. Start a server-side drain before returning the response.

Proposed fix
   const result = streamText({
     model: 'openai/gpt-5.5',
     messages: await convertToModelMessages(messages),
   })

+  void result.consumeStream()
+
   return createUIMessageStreamResponse({
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const result = streamText({
model: 'openai/gpt-5.5',
messages: await convertToModelMessages(messages),
})
return createUIMessageStreamResponse({
stream: toUIMessageStream({
stream: result.stream,
originalMessages: messages,
onEnd: ({ messages: next }) => {
saveChat({ chatId: id, messages: next })
},
}),
})
const result = streamText({
model: 'openai/gpt-5.5',
messages: await convertToModelMessages(messages),
})
void result.consumeStream()
return createUIMessageStreamResponse({
stream: toUIMessageStream({
stream: result.stream,
originalMessages: messages,
onEnd: ({ messages: next }) => {
saveChat({ chatId: id, messages: next })
},
}),
})
🤖 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/comparison/vercel-ai-sdk.md` around lines 875 - 888, Update the stream
handling around result.stream and createUIMessageStreamResponse to start a
server-side drain before returning the response, ensuring the stream continues
to completion after client disconnects so onEnd still invokes saveChat. Preserve
the existing UI response stream and message-saving behavior.

Kiira was pinned to ai@6, so the v7 persistence snippet failed. Bump the checker pins. Migration fences still type-check because v7 keeps the old aliases.

@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.

🧹 Nitpick comments (1)
kiira.config.ts (1)

32-32: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Set the CI Node.js version explicitly.

ai@^7.0.0 requires Node.js >=22. .nvmrc specifies 24.8.0, but no workflow selects .nvmrc or sets node-version. Configure CI with Node.js 22 or newer before pnpm install and pnpm test:docs.

🤖 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 `@kiira.config.ts` at line 32, Update the CI workflow to explicitly select
Node.js 22 or newer, preferably using the repository’s .nvmrc value, before the
pnpm install and pnpm test:docs steps. Ensure the workflow’s setup-node
configuration applies to those commands.

Source: MCP tools

🤖 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.

Nitpick comments:
In `@kiira.config.ts`:
- Line 32: Update the CI workflow to explicitly select Node.js 22 or newer,
preferably using the repository’s .nvmrc value, before the pnpm install and pnpm
test:docs steps. Ensure the workflow’s setup-node configuration applies to those
commands.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c70734be-9a41-44b0-b0de-561807800678

📥 Commits

Reviewing files that changed from the base of the PR and between 43dfa54 and 6143335.

📒 Files selected for processing (1)
  • kiira.config.ts

@AlemTuzlak
AlemTuzlak merged commit 053333d into main Aug 12, 2026
9 checks passed
@AlemTuzlak
AlemTuzlak deleted the docs/comparison-persistence-durability-adapters branch August 12, 2026 16:43
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