Persistence (Advanced)
- Overview
- Quick Start
- Providers
- 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)
Provider Skills
Provider Skills are hosted, provider-managed capability bundles that the model loads on demand and runs inside the provider's server-side sandbox. You reference them by a skill ID; the provider handles installation and execution.
Not to be confused with @tanstack/ai-code-mode-snippets, whose snippets are TypeScript functions your application generates and runs in its own Code Mode sandbox (a local JS isolate). Provider Skills run entirely on the provider's infrastructure.
Skills are inert without an execution tool. The execution tool activates the sandbox; skills are additional bundles that run inside it:
- Anthropic: skills attach to codeExecutionTool (@tanstack/ai-anthropic/tools).
- OpenAI: skills nest inside shellTool (@tanstack/ai-openai/tools) and require the Responses API.
You already have a chat() call working. By the end of this page you will have attached a hosted skill to the right execution tool, with the provider handling the rest.
Portable vs hosted skills#
There are two ways to give a model skills in TanStack AI, and they solve different problems:
- Portable skills (withSkills) render a catalog and let the model call load_skill. They run on any tool-calling model, need no server sandbox, and read SKILL.md from a folder, a bundle, or your own store. Reach for these first.
- Hosted (provider) skills, this page, run inside the provider's server-side sandbox and are referenced by ID. They are non-portable and require an execution tool, but the provider does the running.
Use hosted skills when you need the provider's sandbox (running code, producing files). Use portable skills for everything else.
The two do not mix in one chat() call. If you attach hosted skills to a code_execution or shell tool and also add withSkills, the middleware throws: the model would see two catalogs and two protocols. Pick one delivery mode per call.
Anthropic: skills via codeExecutionTool#
1. Install the package#
npm i @tanstack/ai-anthropicpnpm add @tanstack/ai-anthropicyarn add @tanstack/ai-anthropicbun add @tanstack/ai-anthropic2. Add the codeExecutionTool with skills#
Import codeExecutionTool from @tanstack/ai-anthropic/tools, not from the adapter root. Pass a skills array as the second argument.
import { chat, toServerSentEventsResponse } from '@tanstack/ai'
import { anthropicText } from '@tanstack/ai-anthropic'
import { codeExecutionTool } from '@tanstack/ai-anthropic/tools'
export async function POST(request: Request) {
const { messages } = await request.json()
const stream = chat({
adapter: anthropicText('claude-sonnet-4-5'),
messages,
tools: [
codeExecutionTool(
{ type: 'code_execution_20250825', name: 'code_execution' },
{
skills: [{ type: 'anthropic', skill_id: 'pptx', version: 'latest' }],
},
),
],
})
return toServerSentEventsResponse(stream)
}The adapter automatically:
- Lifts your skills into the request's top-level container.skills parameter (the shape Anthropic's API requires).
- Attaches the code-execution-2025-08-25 beta header, plus the skills-2025-10-02 beta header when skills are present.
You do not set beta headers manually.
Skill shape#
Each entry in the skills array is an AnthropicContainerSkill:
| Field | Type | Required | Notes |
|---|---|---|---|
| type | 'anthropic' | 'custom' | yes | 'anthropic' for Anthropic-hosted skills; 'custom' for your own bundles. |
| skill_id | string | yes | 1–64 characters. |
| version | string | no | Specific version string, or 'latest' (default when omitted). |
Up to 8 skills per request. The factory throws at call time if you exceed this or supply an invalid skill_id.
Deprecation notice#
Setting skills via modelOptions.container.skills is deprecated. Use codeExecutionTool(config, { skills }) instead — the legacy path bypasses the automatic beta-header wiring.
OpenAI: skills via shellTool (Responses API only)#
The OpenAI shellTool accepts an environment object that can carry a skills array. This is Responses API only; the Chat Completions API does not support the shell tool.
1. Install the package#
npm i @tanstack/ai-openaipnpm add @tanstack/ai-openaiyarn add @tanstack/ai-openaibun add @tanstack/ai-openai2. Add the shellTool with skills#
import { chat, toServerSentEventsResponse } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
import { shellTool } from '@tanstack/ai-openai/tools'
export async function POST(request: Request) {
const { messages } = await request.json()
const stream = chat({
adapter: openaiText('gpt-5.2'),
messages,
tools: [
shellTool({
environment: {
type: 'container_auto',
skills: [
{ type: 'skill_reference', skill_id: 'skill_abc', version: '2' },
],
},
}),
],
})
return toServerSentEventsResponse(stream)
}Skill shape#
Each entry in the skills array is a SkillReference:
| Field | Type | Required | Notes |
|---|---|---|---|
| type | 'skill_reference' | yes | Always 'skill_reference' for OpenAI. |
| skill_id | string | yes | The skill identifier provided by OpenAI. |
| version | string | no | A positive integer as a string (e.g. '2') or 'latest'. |
Note: version is a string, not a number.
Scope#
Only hosted, managed-by-id skills are wired by these factories:
- Anthropic: type: 'anthropic' or type: 'custom'
- OpenAI: type: 'skill_reference'
Inline bundles, local-path references, and upload-API skill creation are not handled by codeExecutionTool or shellTool.
Related pages#
- Portable Agent Skills — the provider-agnostic alternative: a catalog plus load_skill, on any tool-calling model.
- Provider Tools — all native provider tools and the type-level guard that prevents pairing a tool with an unsupported model.
- Anthropic adapter → codeExecutionTool
- OpenAI adapter → shellTool