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
Per-Model Type Safety
Provider options are typed from the model you pass to the adapter. An option that model does not support is a type error.
TanStack AI gives modelOptions the type of the exact model that you select. Your editor shows only the options that the model has, and TypeScript rejects the others.
Quick setup#
There is nothing to turn on. Pass the model to the adapter factory, then write modelOptions:
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
// gpt-6.1-sol supports structured outputs, so `text` is allowed.
const stream = chat({
adapter: openaiText('gpt-6.1-sol'),
messages: [{ role: 'user', content: 'Give me a short answer.' }],
threadId: 'thread-1',
modelOptions: {
text: {
format: {
type: 'json_schema',
name: 'my_schema',
schema: {
type: 'object',
properties: { answer: { type: 'string' } },
required: ['answer'],
additionalProperties: false,
},
},
},
},
})Now pick a model without structured outputs. The same option is a type error:
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
// gpt-4-turbo does not support structured outputs, so `text` is rejected.
const stream = chat({
adapter: openaiText('gpt-4-turbo'),
messages: [{ role: 'user', content: 'Give me a short answer.' }],
threadId: 'thread-1',
modelOptions: {
text: {}, // TypeScript error: 'text' does not exist in type
},
})TypeScript reports:
error TS2353: Object literal may only specify known properties, and 'text' does not exist in type ...'.Tip
For structured output, use chat({ outputSchema }) in most apps. It works with every provider and validates the result. The raw text option is for control that is specific to the provider. See Structured Outputs.
What you get#
- Wrong model options fail at compile time, not in production.
- Autocomplete shows only the options of the selected model.
- The types document what each model can do.
- There is no runtime cost. All checks happen at compile time.
How it works#
Each adapter factory keeps the model name as a type parameter: openaiText<TModel>(model). So the adapter knows the exact model at the type level.
chat() looks up the options for that model in a map for each model (ResolveProviderOptions<TModel>). Each entry lists only the options that the model supports. A model without structured outputs has no text property in its options type. TypeScript then rejects text as an unknown property.
The same types also work in these places:
- Typed Pre-Configured Options: keep the options for one model in a reusable object.
- Extend Adapter: attach typed modelOptions to your own models.