All eight scores Source on GitHub
Scorecard · Vercel AI SDK 7

Write it inline & it's fine.
Name the type & it breaks.

ai 7.0.30 · claude-opus-5

AI coding assistants write library code from memory. When a library ships a big release that renames or removes things, the assistant keeps writing the old version. It reads fine & it doesn't build.

SDKProof measures how often that happens. It gives a model real coding jobs for one library, then compiles every answer against the real installed package with tsc, the TypeScript compiler. A task passes only if it compiles — no AI judges another AI.

This page is the Vercel AI SDK, the ai package. Fourteen ordinary jobs — generate some text, stream it, call a tool, get structured output back — one shot each, no docs, no retries.

10 of the 14 compiled. All four misses are the same move: the task asks for a callback pulled out into its own named function, which means writing down its type, and v7 deleted the type names the model reaches for.

This measures the model, not the AI SDK.

71/100 10 of 14 compiled
10 compiled 4 did not compile 0 refused
Model: claude-opus-5 Tasks: 14 Package: ai 7.0.30 Run: 18 August 2026 Pass: it compiles
What went wrong

Four tasks, four type names that no longer exist

Every failure has the same shape. The model names a type that v7 removed. None of the four carried a deprecation warning in v6, so nothing in the training data ever said to stop using them — they were correct one release and gone the next.

Task: type an extracted onFinish handler ai 7.0.30
import type {
  StreamTextOnFinishCallback
} from "ai";

export const handleFinish:
  StreamTextOnFinishCallback<never> =
  async ({ text, usage }) => {
    log(text, usage);
  };

error TS2724: '"ai"' has no exported member named 'StreamTextOnFinishCallback'. Did you mean 'StreamObjectOnFinishCallback'? error TS7031: Binding element 'text' implicitly has an 'any' type.

What v7 compiles ai 7.0.30
// v7 deleted the name. Leave the callback
// inline and TypeScript works out the type:

await streamText({
  model,
  prompt,
  onFinish: async ({ text, usage }) => {
    log(text, usage);
  },
});

tsc --noEmit — 0 errors

Two tasks: naming the telemetry & tool types ai 7.0.30
import type {
  TelemetrySettings,
  ToolCallOptions,
} from "ai";

export const telemetry:
  TelemetrySettings = {
    isEnabled: true,
  };

error TS2305: Module '"ai"' has no exported member 'TelemetrySettings'. error TS2305: Module '"ai"' has no exported member 'ToolCallOptions'.

What v7 compiles ai 7.0.30
import type {
  TelemetryOptions,
  ToolExecutionOptions,
} from "ai";

export const telemetry:
  TelemetryOptions = {
    isEnabled: true,
  };

tsc --noEmit — 0 errors

The compiler sends you to the wrong function. StreamTextOnAbortCallback, StreamTextOnChunkCallback and StreamTextOnErrorCallback all survived v7 — only the finish ones went. So the did-you-mean for the missing StreamTextOnFinishCallback is StreamObjectOnFinishCallback — a different function's callback. A developer who takes the suggestion ends up with streamObject's type while writing streamText. StreamTextOnStepFinishCallback fails the same way and points at GenerateTextOnStepFinishCallback.

Then the second error lands. Once the type is gone, the callback's arguments have nothing describing them, so tsc reports text as an implicit any. One missing name produces a page of errors.

Reading the errors. TS2724 is "that export doesn't exist, did you mean this one". TS2305 is "that export doesn't exist" with no suggestion at all. TS7031 is "I can't tell what type this argument is". All three are the compiler's own words.

The other 10

Everything that lets TypeScript work it out compiles

Text generation, streaming, structured output, embeddings, message arrays, the renamed maxOutputTokens and the whole tool API all type-check against the real v7.

Write the callback inline and TypeScript infers its type from where it sits — the removed names never come up, so the code compiles. The four failures all start the same way: someone pulls a handler out into its own function and now has to name the type by hand.

Which makes this a narrow, specific gap rather than a general one. The model knows v7. It doesn't know which type names v7 kept.

generateText streamText generateObject with zod embed system prompt messages array temperature maxOutputTokens tool API — inputSchema / stopWhen
One library, three readings

The number closed, then re-opened

This library has now shown the whole cycle. A tool rename in mid-2025 was a gap the previous model fell into: 90. Opus 5, trained later, had absorbed it: 100. Then v7 shipped in June 2026, removed four exported type names, and the same model scores 71.

Nothing about the model got worse. The gap closed and a new release opened a new one, on one library, over about a year. That's why a single number is worth less than the direction it's moving.

What decides the size of the gap is how much warning there was. An API marked deprecated for a year or two gets absorbed long before it's deleted — the model has read a thousand migration notes about it. An API that's correct in one release and gone in the next leaves no trail. None of these four names carried a deprecation warning, which is why they're still being written.

React Router shows the other side of that: it removed a lot in v8 and still scores 93, because most of those removals had been signposted for years.

How this was measured

The compiler has the last word

Fourteen realistic AI SDK tasks, written by claude-opus-5, each dropped into a small project with ai 7.0.30, @ai-sdk/openai and zod actually installed, then run through tsc --noEmit. A task passes only if it compiles. The prompts name the function to write, never the type names.

One model
Everything here is claude-opus-5. Another model will score differently — this same set scored 90 on Opus 4.8, against an earlier version of the package.
One shot, no docs
The model gets the task and nothing else. Paste v7's migration notes in and these four misses would almost certainly go.
Small numbers
Fourteen tasks, four of which probe the same thing. Read 71 as "there is one specific hole", not as a general grade.
Compiles is not correct
tsc, the TypeScript compiler, checks that the API exists and the types line up. It never runs the code, so nothing here says a stream behaves properly.
Refusals
A refusal is neither a pass nor a fail — the model writes no code, so there is nothing to compile, and refused tasks drop out of the denominator. This run had none. Stripe had five, which is why its 100 is out of 10 rather than 15.
It measures the model
71 is a statement about what claude-opus-5 remembers of the AI SDK, not about the AI SDK's design.
Elsewhere

Related findings

Agent docs One sentence from a library's own docs fixes the failure 10 times out of 10. The same sentence buried in their full docs pack fixes nothing. Read the numbers → Agent skills Three libraries ship files meant for AI agents. Scored with and without them, across six runs, not one difference clears zero. Read the numbers →
The rest of the board

Other libraries, scored the same way

All eight scores are on the home page →

Score my library

Name any TypeScript package & I'll run it. Or do it yourself — it's all open source.

There's no npm package. Clone the repo, point it at a library, run it. The compiler is the judge.