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:
- Emendant reported 2 changes at 4 places in 4 files. One is a deterministic rename, and Emendant wrote that patch itself. The other needs a model provider, and we did not name one.
- The migration changed 17 files. We moved v3 provider types to v4 in 13 files and checked the changed message behaviour with a two-step run.
- The final checks passed, including 262 tests, and the final scan reported no findings.
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
npx emendant@0.5.0 scan --feed-snapshot 2026-09-23.8The 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 filesWith --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
npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8 --dry-runThe 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
npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8fix 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 nothingThe 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:
npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8 --no-verifygit apply .emendant/patches/ai-npm-7.0.0-stepcountis-renamed.patchrm -rf .emendant emendant.jsonThe patch applies cleanly.
Step 5: upgrade the dependencies
Emendant does not change dependency versions. We did that step ourselves:
npm install @ai-sdk/provider@4.0.0npm install -D ai@7.0.0 @ai-sdk/anthropic@4.0.0 @ai-sdk/google@4.0.0 @ai-sdk/openai@4.0.0npm 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:
perl -pi -e 's/LanguageModelV3/LanguageModelV4/g' $(git grep -l LanguageModelV3 -- src)perl -pi -e 's/specificationVersion: "v3"/specificationVersion: "v4"/' src/model.tsThat 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:
npx biome check --write src/agent.ts src/spine.tsStep 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:
perl -pi -e 's/result\.response\.messages/result.responseMessages/' src/agent.tsStep 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:
perl -pi -e 's/ \?\? u\.cachedInputTokens//' evals/draco.tsnpx biome check --write evals/draco.tsAfter 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:
- It found the
stepCountIsrename at both sites and wrote the patch. The patch applied cleanly, and the final typecheck and tests pass with it. Lint needed the import order fixed, in step 7. It was gradedstructurally checkedonly, because verification withheld it twice for reasons outside the patch. - It found the provider types change and said it needs a model. One of its two sites is the repository’s own mock, the case the entry’s medium confidence warns about. The other is in the function that broke.
- It did not change any dependency version, and it did not run lint.
What Emendant did not report:
- The
wrapLanguageModelreturn type and the v3 middleware insrc/model.ts. The typecheck found it. - The change to
result.response.messages. Nothing found it except reading the release’s declarations. - The
cachedInputTokensread inevals/draco.ts. The feed covers the rename, but not a usage object that arrives as a parameter.
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
- We did not name a model provider, so we did not test the assisted patch for the provider types change.
- We did not run atlas against a live model, a search API or a browser. Its tests use mock models. A green test suite here does not show that a real research run behaves as before.
- We ran on macOS with Node.js 22.23.3 only. atlas CI also runs Node.js 20, which we did not test.
src/providers/fetch.test.tsdid not run in Emendant’s sandbox. It did run, and passed, in every check outside the sandbox.- This is one repository and one release. It shows how one migration went. It does not measure how often Emendant finds or misses a change.
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.
git clone https://github.com/steel-dev/atlas.gitcd atlasgit checkout 7aea679b740afa97b26617e53e5c1f2e1429eec5npm cinpm run lint && npm run typecheck && npm run test:run && npm run build
npx emendant@0.5.0 scan --feed-snapshot 2026-09-23.8npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8 --dry-runnpx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8printf '{\n "testCommand": "npx vitest run --exclude src/providers/fetch.test.ts"\n}\n' > emendant.jsonnpx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8
npx emendant@0.5.0 fix --feed-snapshot 2026-09-23.8 --no-verifygit apply .emendant/patches/ai-npm-7.0.0-stepcountis-renamed.patchrm -rf .emendant emendant.json
npm install @ai-sdk/provider@4.0.0npm install -D ai@7.0.0 @ai-sdk/anthropic@4.0.0 @ai-sdk/google@4.0.0 @ai-sdk/openai@4.0.0npm 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.tsperl -pi -e 's/result\.response\.messages/result.responseMessages/' src/agent.tsperl -pi -e 's/ \?\? u\.cachedInputTokens//' evals/draco.tsnpx biome check --write src/agent.ts src/spine.ts evals/draco.ts
npm run lint && npm run typecheck && npm run test:run && npm run buildnpx emendant@0.5.0 scan --feed-snapshot 2026-09-23.8We 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.