vercel/add-harness-package
Guide for adding new AI SDK harness packages. Use when creating a new @ai-sdk/harness-<name> package that adapts a coding-agent runtime to HarnessV1.
npx skills add https://github.com/vercel/ai --skill add-harness-package
This guide covers creating a new @ai-sdk/harness-<name> package for an agent harness.
A harness can be host-driven, where the runtime runs in the host process and uses the sandbox remotely, or bridge-backed, where a small bridge runs inside the sandbox because the runtime needs local access to the sandbox filesystem or process environment.
Prefer host-driven when the runtime supports it.
@ai-sdk/harness-<name> packages: Create an issue first to discuss whether the runtime belongs in this repo.See https://github.com/vercel/ai/pull/16255/changes for a complete example of adding a new harness.
The AI SDK uses a layered harness architecture following the adapter pattern:
@ai-sdk/harness): Defines interfaces like HarnessV1 and HarnessV1Session@ai-sdk/harness/utils): Shared code for implementing harnesses@ai-sdk/harness-<name>): Concrete adapters for harnesses@ai-sdk/harness/agent): The high-level user-facing HarnessAgent APICreate packages/harness-<name> with this baseline structure:
packages/harness-<name>/
├── src/
│ ├── index.ts
│ ├── <name>-harness.ts
│ ├── <name>-harness.test.ts
│ └── <name>-auth.ts # if the runtime needs auth resolution
├── package.json
├── tsconfig.json
├── tsconfig.build.json
├── tsup.config.ts
├── turbo.json
├── vitest.node.config.js
└── README.md
If the runtime must execute inside the sandbox, add bridge files as well:
src/
├── <name>-bridge-protocol.ts
├── <name>-bridge-protocol.test.ts
└── bridge/
├── index.ts
├── package.json
└── pnpm-lock.yaml
Add a CHANGELOG.md containing just the package heading (# @ai-sdk/harness-<name>). Every package is required to have one.
Use existing harness packages as the source of truth for scripts, exports, repository metadata, and publish settings.
Required package basics:
"name": "@ai-sdk/harness-<name>""type": "module""version": "0.0.0" (starting point for new packages)"license": "Apache-2.0""sideEffects": false@ai-sdk/harness via workspace:*@ai-sdk/provider-utils via workspace:* when using sandbox/auth/schema utilities"engines": { "node": ">=22" }For bridge packages, add any bridge asset copy step required for files under src/bridge/.
Bridge dependency rules (bridge-backed harnesses):
src/bridge/package.json (installed in-sandbox at bootstrap), not the main package.json. After changing them, regenerate src/bridge/pnpm-lock.yaml with pnpm --dir packages/harness-<name>/src/bridge install --lockfile-only --ignore-workspace (runnable from the repo root).src/bridge/, keep three things in sync: the import, the external array in tsup.config.ts, and the dep in src/bridge/package.json. A missing entry shows up only at sandbox runtime as a module-resolution error.@anthropic-ai/sdk, openai) resolved from the model id at runtime — even though nothing imports them directly. These fail only when a model of that provider is actually used.Copy the nearest existing harness package config files and adjust paths/package names:
tsconfig.jsontsconfig.build.jsontsup.config.tsturbo.jsonvitest.node.config.jsHarness packages currently use Node tests only unless the implementation has a specific reason to add another runtime.
Export a factory from <name>-harness.ts and re-export it from src/index.ts.
Use the architecture doc for contract details. At implementation time, verify:
HarnessV1 with specificationVersion: 'harness-v1';harnessId;builtinTools;startOpts.sandboxSession and startOpts.sessionWorkDir; never create a separate sandbox;HarnessCapabilityUnsupportedError from the method that needs an unsupported runtime capability;modelId consistent with what's actually sent (don't report a model the bridge silently overrode);tools and instructions that doPromptTurn/doContinueTurn may receive: if the runtime can't take custom tools, throw HarnessCapabilityUnsupportedError so it's obvious rather than silently dropped; if it has no native instructions input, prepend them to the first user message (the Codex/Claude Code workaround);workDir, bridge-state dir, …) when building shell commands for sandbox.run/sandbox.spawn — they can contain spaces.If the runtime needs in-sandbox setup, expose getBootstrap().
Add only the concerns the runtime needs:
getAiGatewayAuthFromEnv() helper rather than reading env directly. This ensures both VERCEL_OIDC_TOKEN and AI_GATEWAY_API_KEY are accepted as Gateway credential. When the runtime resolves provider per model, resolve the provider from the model id and set that provider's env; if routing through the gateway, note that base-URL conventions differ per provider (e.g. an Anthropic client appends /v1/messages to a root base, an OpenAI client appends to a /v1 base);ai-sdk/harness-<name>/${VERSION} and use it for Gateway requests. Configure both User-Agent and x-client-app headers when the underlying runtime/SDK supports both; at least one of those two headers is required. If the runtime cannot set arbitrary headers directly, use the runtime-supported equivalent that produces one of those headers (e.g. an SDK client-app environment variable);items, enums, descriptions); a flat top-level-only conversion silently drops the model's structured guidance. Passing the JSON Schema through directly, if the runtime accepts it, avoids the problem;Certain structural conventions for harness adapters are being enforced via the konsistent CLI.
Run pnpm konsistent once you're done to check for those. Fix any violations flagged before proceeding.
Add focused Node tests for:
Use mocked sandbox sessions and bridge/runtime boundaries where possible. Do not require live provider credentials in unit tests.
getBootstrap() reads the _compiled_ bridge assets (e.g. dist/bridge/index.mjs), which don't exist when tests run against src, so a test that calls it will hit ENOENT. Mock node:fs/promises readFile for the bridge asset paths (see the Codex/OpenCode harness tests for the pattern).
Keep README short:
HarnessAgent usage;Link to the main harness docs for broader concepts.
Add relevant examples for the new harness.
examples/ai-functions when the harness package needs a scriptable provider-behavior example.examples/harness-e2e-next (Next.js) and examples/harness-e2e-tui (TUI).Create documentation in content/providers/02-ai-sdk-harnesses/<next number>-<name>.mdx.
Include:
Update content/docs/03-ai-sdk-harnesses/05-harness-adapters.mdx to list the new harness when it is ready to be public.
Run from the workspace root:
pnpm konsistent
pnpm update-references
pnpm --filter @ai-sdk/harness-<name> build
pnpm --filter @ai-sdk/harness-<name> test
pnpm type-check:full
Add a changeset with pnpm changeset. For a brand-new harness package's first release, use major (not the usual patch), matching the other harness packages.
Run relevant harness examples against a live sandbox early — don't rely on unit tests and type-check alone. Runtime API constraints (e.g. unexpected config-option rejections, the exact streaming event names the runtime emits, gateway base-URL format) surface only when the bridge actually drives the runtime, and they're far cheaper to find before the docs/examples are built on top.
packages/harness-<name>package.json configured with correct dependenciestsconfig.json, tsconfig.build.json)tsup.config.ts)vitest.node.config.js)User-Agent and/or x-client-app client attributionsessionWorkDirCHANGELOG.md added (package heading; required by konsistent)major for a first release)content/providers/02-ai-sdk-harnesses/pnpm update-references runpnpm type-check:full from root)@ai-sdk/harness READMETake vercel/add-harness-package from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.