We migrated steel-dev/atlas, a public deep research library, from ai 6.0.194 to AI SDK 7.0.0 and its three provider packages from 3.x to 4.0.0. Emendant identified two changes at four code sites and prepared the rename patch. After that patch and the additional developer changes described below, lint, typecheck, tests and build all passed.

The measured outcome:

Evidence and scope

Emendant’s verification withheld the rename patch on both runs for the reasons recorded in steps 2 and 3. We applied it after the structural check and ran the final repository checks ourselves. The typecheck found an additional provider-type change, and a declaration review found a message behaviour change that passed the existing tests. The later sections record these gaps and the additional changes. This is a measured migration, not a claim that a clean scan covers every upgrade step.

The starting state

Repository steel-dev/atlas, MIT licence
Commit 7aea679b740afa97b26617e53e5c1f2e1429eec5, 7 July 2026
Installed SDK ai 6.0.194, @ai-sdk/anthropic 3.0.81, @ai-sdk/google 3.0.80, @ai-sdk/openai 3.0.67, @ai-sdk/provider 3.0.10
Target ai 7.0.0, the three providers and @ai-sdk/provider at 4.0.0
Emendant emendant@0.5.0 from npm
Feed snapshot 2026-09-23.8, pinned on every command
Runtime Node.js 22.23.3, npm 11.12.1, macOS on Apple silicon
Date 29 September 2026

Before any change, the checks atlas runs in its own CI all pass: npm run lint, npm run typecheck, npm run test:run (35 test files, 262 tests passed, 1 skipped) and npm run build. npm run check:exports also passes. npm run eval:typecheck is not in atlas CI, and it already fails at this commit with 93 errors. We used it only to compare before and after.

Node.js 22 matters here. atlas has an optional native dependency, isolated-vm 6.1.2. On Node.js 25.9.0, its test file crashes the Vitest worker before any SDK change, so the baseline is not green.

Step 1: scan

Terminal window
npx emendant@0.5.0 scan --feed-snapshot 2026-09-23.8

The scan reads 107 files and reports two changes:

@ai-sdk/anthropic 3.0.81, @ai-sdk/google 3.0.80, @ai-sdk/openai 3.0.67 → 4.0.0
1 change affects this repository, through all three
× breaking models are the v4 provider types, so a v3 annotation stops type checking medium confidence
provider-types-v3-to-v4 assisted fix, written by a model you name
src/model.ts:466:4
src/structured.test.ts:16:35
ai installed 6.0.194 → 7.0.0
1 change affects this repository
! deprecation stepCountIs became isStepCount
stepcountis-renamed deterministic fix, written by Emendant itself
src/agent.ts:1:43
src/spine.ts:5:3
2 changes to make · 4 places in 4 files

With --json, the same result is 4 findings at 8 sites, because the provider change is one finding for each of the three provider packages.

The provider types change has medium confidence, and its guidance says why: the matcher sees that a value is built against the v3 type, not where that value comes from. The two sites show both sides of that. src/model.ts:466 is the return type of atlas’s engineModel, and it is in the function that breaks in step 6. src/structured.test.ts:16 is a test helper that returns atlas’s own MockLanguageModelV3. The guidance says to make no edit to a value like that.

Step 2: preview the patch

Terminal window
npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8 --dry-run

The dry run prints one patch, +7 −7 in 2 files, graded structurally checked. It renames the import and every call:

import { generateText, type ModelMessage, stepCountIs } from "ai";
import { generateText, type ModelMessage, isStepCount } from "ai";
...
stepCountIs(spec.maxTurns ?? rctx.config.envelope.maxTurns),
isStepCount(spec.maxTurns ?? rctx.config.envelope.maxTurns),

The other five changed lines are the same rename in src/spine.ts: the import and four calls. The provider types change gets no patch, because no model provider was named.

Step 3: verify, twice, and both runs withheld

Terminal window
npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8

fix found npm run typecheck and npm run test and ran them in a copy of the repository, in a sandbox with no network. The patch was not offered:

not proved this repository does not pass `npm run test` before any patch is applied, so a run with the patch applied would prove nothing

The cause is one test file. src/providers/fetch.test.ts starts an HTTP server on 127.0.0.1, and the sandbox refuses the listen with EPERM. The file has nothing to do with the SDK, so we told fix to run the tests without it. We put this emendant.json in the repository root:

{
"testCommand": "npx vitest run --exclude src/providers/fetch.test.ts"
}

On the second run, the baseline passed. The copy was moved to ai 7.0.0 and patched, and then npm run typecheck failed, so the patch was again not offered:

not proved `npm run typecheck` did not pass with this patch applied and the release installed.

The verification installs the release the patch describes, ai@7.0.0, and nothing else. The copy still had the v3 provider packages, and the typecheck errors are at the boundary between them, in src/model.ts and src/providers/search.ts. Emendant says that a red typecheck here is not a claim about the patch, and that is right. It is the typecheck of a half-upgraded tree.

On our machine, the result of this second run also depends on how you start Emendant. A second Node.js, 25.9.0 from Homebrew, is installed. When we started Emendant with npx, the sandbox’s npm rebuild ran under Node.js 25.9.0. isolated-vm has no prebuilt binary for that version, and compiling it needs the Node.js headers from the network, so the run stopped at npm rebuild. When we started the same Emendant 0.5.0 from a local install, it got to the typecheck. Both outcomes are inconclusive. Neither says anything about the patch.

Step 4: write the patch and apply it

We wrote the patch without verification and applied it:

Terminal window
npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8 --no-verify
git apply .emendant/patches/ai-npm-7.0.0-stepcountis-renamed.patch
rm -rf .emendant emendant.json

The patch applies cleanly.

Step 5: upgrade the dependencies

Emendant does not change dependency versions. We did that step ourselves:

Terminal window
npm install @ai-sdk/provider@4.0.0
npm install -D ai@7.0.0 @ai-sdk/anthropic@4.0.0 @ai-sdk/google@4.0.0 @ai-sdk/openai@4.0.0
npm pkg set 'peerDependencies.ai=^7.0.0' 'peerDependencies.@ai-sdk/anthropic=^4.0.0' 'peerDependencies.@ai-sdk/google=^4.0.0' 'peerDependencies.@ai-sdk/openai=^4.0.0'

atlas declares the SDK as peer dependencies, so the ranges there change by hand. @ai-sdk/provider is pinned to 4.0.0 because ai@7.0.0 and the three providers depend on exactly that version. With a newer 4.x at the top level, the project would get two copies of the provider types.

Step 6: the typecheck error Emendant did not report

src/model.ts(858,10): error TS2352: Conversion of type 'LanguageModelV4' to type 'LanguageModelV3' may be a mistake ...
src/model.ts(858,44): error TS2322: Type 'LanguageModelV3Middleware' is not assignable to type 'LanguageModelMiddleware | LanguageModelMiddleware[]'.

Line 858 is the last line of engineModel:

return wrapLanguageModel({ model: inner, middleware }) as LanguageModelV3;

In ai 7.0.0, wrapLanguageModel returns a LanguageModelV4. In 6.0.194 it returned a LanguageModelV3. You can see this in the 7.0.0 source and the 6.0.194 source. The 7.0 migration guide does not mention this change. The middleware is annotated LanguageModelV3Middleware, imported from @ai-sdk/provider. The feed has an entry for middleware in this release, but it matches LanguageModelMiddleware imported from ai, and atlas does not use that name. So no finding pointed here. The provider types finding at line 466 is in the same function, but its guidance is about a v3 annotation that receives a provider’s model. It does not say that wrapLanguageModel changed.

The coherent fix is to move the whole wrapper to the v4 types. When we changed only src/model.ts, the change spread to 39 errors in files that pass the model around. So we renamed every v3 language model type in src, including atlas’s test mocks:

Terminal window
perl -pi -e 's/LanguageModelV3/LanguageModelV4/g' $(git grep -l LanguageModelV3 -- src)
perl -pi -e 's/specificationVersion: "v3"/specificationVersion: "v4"/' src/model.ts

That edits 13 files. After it, the typecheck passes.

Step 7: lint

Emendant’s patch renames the specifier where it stands, so isStepCount is out of order in two import lists. atlas uses Biome, and its lint fails on assist/source/organizeImports in src/agent.ts and src/spine.ts. Emendant’s verification runs a typecheck and tests, not lint. Biome’s own safe fix sorts the lists:

Terminal window
npx biome check --write src/agent.ts src/spine.ts

Step 8: the behaviour change that passed every check

With the typecheck and the tests green, one AI SDK 7 change was still in the code. atlas’s research agent returns the messages from its gather step, and the synthesis step uses them as its history:

...(spec.captureMessages ? { messages: result.response.messages } : {}),

In 6.0.194, result.response.messages holds the messages from every step of the call. In 7.0.0, the result declares response as “additional response information from the last step”, deprecated in favour of finalStep.response. The accumulated list is now result.responseMessages. The type is the same, so the typecheck does not see it.

We ran one generateText call with a mock model that makes one tool call and then answers, on both versions:

Steps result.response.messages result.responseMessages
ai 6.0.194 2 assistant, tool, assistant not declared
ai 7.0.0 2 assistant assistant, tool, assistant

Without the fix, atlas would give the synthesis step only the last assistant message. The tool calls and results that the gather agent collected would be lost. atlas’s 262 tests pass with and without the fix. The feed at this snapshot has no entry for this change. The fix is one line:

Terminal window
perl -pi -e 's/result\.response\.messages/result.responseMessages/' src/agent.ts

Step 9: the evaluation script

npm run eval:typecheck still fails with its 93 errors from before the upgrade, and one more:

evals/draco.ts(1142,47): error TS2339: Property 'cachedInputTokens' does not exist on type 'LanguageModelUsage'.

The feed has a deterministic entry for this rename. Its matchers read usage and totalUsage on a value that generateText or streamText returned. In evals/draco.ts, the usage object arrives as a function parameter annotated LanguageModelUsage, so the matcher does not report it. That is the same rule that keeps the entry from reporting a provider’s own usage object. The code already reads inputTokenDetails.cacheReadTokens first, so we removed the fallback:

Terminal window
perl -pi -e 's/ \?\? u\.cachedInputTokens//' evals/draco.ts
npx biome check --write evals/draco.ts

After that, eval:typecheck has the same 93 errors as before the upgrade.

The result

Check Before After
npm run lint passes passes
npm run typecheck passes passes
npm run test:run 262 passed, 1 skipped 262 passed, 1 skipped
npm run build passes passes
npm run check:exports passes passes
npm run eval:typecheck 93 errors the same 93 errors
emendant scan 2 changes, 4 places nothing found, also with --adopted

Without the lockfile, the migration changes 17 files, with 181 lines added and 182 removed. Most of them are the v3 to v4 type rename.

What Emendant did in this migration:

What Emendant did not report:

What we left in place: atlas still passes system to generateText and generateObject, for example at src/agent.ts:80 and src/structured.ts:25, and onStepFinish at src/agent.ts:145. AI SDK 7 renamed these to instructions and onStepEnd and still accepts the old names as deprecated aliases. The feed at this snapshot does not report either option.

Limits of this example

Repeat it

Use Node.js 22 as the first node on your PATH. At a terminal, the first scan asks some setup questions. Do not name a model provider if you want the same result.

Terminal window
git clone https://github.com/steel-dev/atlas.git
cd atlas
git checkout 7aea679b740afa97b26617e53e5c1f2e1429eec5
npm ci
npm run lint && npm run typecheck && npm run test:run && npm run build
npx emendant@0.5.0 scan --feed-snapshot 2026-09-23.8
npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8 --dry-run
npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8
printf '{\n "testCommand": "npx vitest run --exclude src/providers/fetch.test.ts"\n}\n' > emendant.json
npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8
npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8 --no-verify
git apply .emendant/patches/ai-npm-7.0.0-stepcountis-renamed.patch
rm -rf .emendant emendant.json
npm install @ai-sdk/provider@4.0.0
npm install -D ai@7.0.0 @ai-sdk/anthropic@4.0.0 @ai-sdk/google@4.0.0 @ai-sdk/openai@4.0.0
npm pkg set 'peerDependencies.ai=^7.0.0' 'peerDependencies.@ai-sdk/anthropic=^4.0.0' 'peerDependencies.@ai-sdk/google=^4.0.0' 'peerDependencies.@ai-sdk/openai=^4.0.0'
perl -pi -e 's/LanguageModelV3/LanguageModelV4/g' $(git grep -l LanguageModelV3 -- src)
perl -pi -e 's/specificationVersion: "v3"/specificationVersion: "v4"/' src/model.ts
perl -pi -e 's/result\.response\.messages/result.responseMessages/' src/agent.ts
perl -pi -e 's/ \?\? u\.cachedInputTokens//' evals/draco.ts
npx biome check --write src/agent.ts src/spine.ts evals/draco.ts
npm run lint && npm run typecheck && npm run test:run && npm run build
npx emendant@0.5.0 scan --feed-snapshot 2026-09-23.8

We ran this sequence from a fresh clone, with an empty Emendant cache, and got the results above. The one difference was step 3’s second run, as described there. Read the AI SDK migration guide for the other 6.x to 7.0 changes Emendant covers, and the AI SDK 7.0.0 page for the full coverage table.