Persistence (Advanced)
- Build your first harness
- Host options
- Send and show media
- Connect clients
- Write a client in any language
- Access and tenants
- Run in the terminal
- Deploy a harness
- Send inputs safely
- Run a tool without a model
- Durable sessions
- Run background work on workers
- List, rename, and fork sessions
- Lifecycle and retention
- Run side effects once
- Wait for a person
- Build on the session log
- Control how a turn ends
- Store settings per thread
- Compact a long session
- Fork and reset a thread
- One log for a session tree
- Write a plugin
- Track usage and cost
- Ask before risky tool calls
- Give your agent coding tools
- Sandbox and extend the coding tools
- Run the coding tools for many threads
- Undo a turn
- Build a coding agent
- Delegate to coding agents
- Switch between agent profiles
- Auth and connectors
- Share a thread between people
- Connect model providers
- Build your own UI
- Code mode in a harness
- Work until a goal is met
- Run agents from a harness
- Hand off a conversation
- Add skills
- Use MCP servers
- Give your agent a computer
- Use from any MCP client
- Use a harness from an editor (ACP)
- Overview
- Quick Start
- ProvidersUpdated
- Harnesses
- Harness Auth
- Workspace
- Tools
- Policy
- Lifecycle & Snapshots
- Portable Snapshots
- Keep Files After Reload
- Save a Named Version
- Branch From a Version
- Send a Frozen File
- Let the Agent Save and Fork
- Pick Which Files to Keep
- What a Snapshot Stores
- Instance Durability
- Durable Runs
- Events
Sandboxes (Advanced)
- Runtime Context
- Bring Your Own Key
- Keep Keys on the Server
- Debug Logging
- Multimodal Content
- Files API
- Per-Model Type Safety
- Runtime Adapter Switching
- Tree-Shaking
- Extend Adapter
- Extend Adapter: Advanced
- Typed Pre-Configured Options
- Test with a Fake Model
- Prompt Caching
- Mid-Conversation Changes
- Approval Flow Processing
Tree-Shaking
Import one adapter from its package subpath. The bundle then omits image generation and the adapters you do not call.
TanStack AI exports each activity and each adapter as a separate named function. Import only the ones you use, and your bundler drops the rest.
Quick setup#
- Import the activity that you use from @tanstack/ai.
- Import the one adapter that you use from the provider package.
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
const stream = chat({
adapter: openaiText('gpt-6.1-sol'),
messages: [{ role: 'user', content: 'Hello!' }],
threadId: 'thread-1',
})
for await (const chunk of stream) {
console.log(chunk)
}Your bundle now holds:
- The chat function, with its streaming and tool code.
- The openaiText adapter.
It does not hold summarize, generateImage, the other activities, or the adapters of other providers.
Use named imports#
Import each function by name. Some bundlers cannot drop the unused exports of a namespace import:
// Good: only what you use
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'// Avoid: a namespace import can pull in every export
import * as ai from '@tanstack/ai'
import * as openai from '@tanstack/ai-openai'Use more than one activity#
Import each activity and its adapter. Each one is independent:
import { chat, summarize } from '@tanstack/ai'
import { openaiSummarize, openaiText } from '@tanstack/ai-openai'
const stream = chat({
adapter: openaiText('gpt-6.1-sol'),
messages: [{ role: 'user', content: 'Hello!' }],
threadId: 'thread-1',
})
const summary = await summarize({
adapter: openaiSummarize('gpt-5.4-mini'),
text: 'Long text to summarize.',
threadId: 'thread-1',
})The create*Options helpers are separate exports too. See Typed Pre-Configured Options.
Tips for smaller bundles#
- Use a specific adapter function, for example openaiText, for each activity.
- Put different activities in different API routes.
- Load code-split routes with dynamic imports.
- In React Native and Expo, keep the chat screens client-only. Import useChat and the connection adapters there. Do not import provider SDKs, server response helpers, React DOM UI, devtools UI, or other framework packages. See Quick Start: React Native for the server-only provider boundary and the mobile transport setup.
How it works#
- Each function is a named ES module export, not a default export.
- Each activity and adapter lives in its own module.
- @tanstack/ai declares "sideEffects": false in its package.json, so bundlers can drop modules that you do not import.
- Type-only imports are removed at build time.
Bundlers such as Vite, Webpack, Rollup, and esbuild follow the static imports and drop the code that you do not reference.
Advanced#
Activity functions#
Each activity is a separate export of @tanstack/ai:
| Function | Activity |
|---|---|
| chat | Chat and text generation |
| summarize | Summarization |
| generateImage | Image generation |
| generateSpeech | Text-to-speech |
| generateTranscription | Audio transcription |
| generateVideo | Video generation |
| generateLiveVideo | Live video sessions |
| generateWorld | Live world sessions |
Adapter functions by provider#
Each provider package exports one function for each activity. Some examples:
| Package | Adapters |
|---|---|
| @tanstack/ai-openai | openaiText, openaiSummarize, openaiImage, openaiSpeech, openaiTranscription, openaiVideo, openaiEmbedding |
| @tanstack/ai-anthropic | anthropicText, anthropicSummarize |
| @tanstack/ai-gemini | geminiText, geminiSummarize, geminiImage, geminiSpeech (experimental), geminiEmbedding |
| @tanstack/ai-ollama | ollamaText, ollamaSummarize, ollamaEmbedding |
Each adapter page lists the full set for its provider.
Adapter types#
Each adapter type has its own interface and a kind property:
| Interface | Main method | kind |
|---|---|---|
| TextAdapter | chatStream() | 'text' |
| SummarizeAdapter | summarize() | 'summarize' |
| ImageAdapter | generateImages() | 'image' |
| TTSAdapter | generateSpeech() | 'tts' |
| TranscriptionAdapter | transcribe() | 'transcription' |
| VideoAdapter | createVideoJob(), getVideoStatus(), getVideo() | 'video' |
| LiveVideoAdapter | createLiveVideo() | 'liveVideo' |
| WorldAdapter | createWorld() | 'world' |
import { openaiSummarize, openaiText } from '@tanstack/ai-openai'
const chatAdapter = openaiText('gpt-6.1-sol')
console.log(chatAdapter.kind) // 'text'
const summarizeAdapter = openaiSummarize('gpt-5.4-mini')
console.log(summarizeAdapter.kind) // 'summarize'Type safety#
Separate exports keep the full types. Each adapter knows the models of its provider:
import { openaiText, type OpenAIChatModel } from '@tanstack/ai-openai'
const adapter = openaiText('gpt-6.1-sol')
const model: OpenAIChatModel = 'gpt-6.1-sol' // OK
const model2: OpenAIChatModel = 'invalid' // Type error