asynthetic 0.1.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # Asynthetic
2
+
3
+ **Verified migration maps for AI coding agents, served over MCP.**
4
+
5
+ LLMs suffer from temporal drift: training data mixes many versions of a library, so an agent confidently writes v4 syntax into a v5 codebase. Asynthetic is an MCP (Model Context Protocol) server that tells agents **exactly what breaks between two versions of a library and how to fix it** — from hand-curated, source-cited migration maps instead of stale model memory.
6
+
7
+ > Context7 tells the agent what the current API is. **Asynthetic tells the agent what changed and how to migrate.**
8
+
9
+ ## How it works
10
+
11
+ Every migration map is hand-curated from authoritative sources only — official migration guides, GitHub releases, and framework blogs — and every map carries:
12
+
13
+ - ordered breaking changes with **before/after code snippets**
14
+ - a `category` per change (`signature-change`, `removal`, `rename`, `behavior-change`, `config-change`, `import-change`, `deprecation`)
15
+ - deprecations with replacement symbols and removal timelines
16
+ - **`source_urls` citations** and a **`last_verified` date**
17
+
18
+ Maps are never LLM-generated. A map that would be wrong is worse than no map, so when nothing verified matches a query, the server says so explicitly and instructs the agent **not** to fabricate migration steps.
19
+
20
+ ## Tools
21
+
22
+ | Tool | Returns |
23
+ |---|---|
24
+ | `get_migration(package, from_version, to_version, ecosystem?)` | The full migration map for the requested upgrade window |
25
+ | `get_breaking_changes(package, version, ecosystem?)` | Breaking changes introduced when upgrading **to** a version |
26
+ | `check_compatibility(...)` | Stub — always returns `implemented: false` (planned) |
27
+
28
+ Version arguments accept concrete versions (`14.2.35`), partial versions (`14`), or SemVer ranges (`^14.2.0`, `~4.3.0`, `15.x`). Resolution is exact-first, then SemVer-aware, and always disclosed in the response via `match_type`, `resolved_via` (`exact_string` / `semver_range` / `major_version`), and a `match_note`.
29
+
30
+ ## Current coverage
31
+
32
+ | Package | Migration | Breaking changes |
33
+ |---|---|---|
34
+ | `@modelcontextprotocol/sdk` | 1.x → 2.0 | 21 |
35
+ | `ai` (Vercel AI SDK) | 4.x → 5.0 | 23 |
36
+ | `next` (Next.js) | 14 → 15 | 17 |
37
+
38
+ Coverage is deliberately narrow and deep: fast-moving AI and JavaScript-ecosystem frameworks, curated for correctness over breadth.
39
+
40
+ ## Quick start
41
+
42
+ ### Hosted server (HTTP)
43
+
44
+ ```sh
45
+ claude mcp add --transport http asynthetic https://asynthetic.up.railway.app/mcp
46
+ ```
47
+
48
+ Or in any client that takes a JSON MCP config:
49
+
50
+ ```json
51
+ {
52
+ "mcpServers": {
53
+ "asynthetic": {
54
+ "url": "https://asynthetic.up.railway.app/mcp"
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ ### Local (stdio via npm)
61
+
62
+ ```sh
63
+ claude mcp add asynthetic -- npx -y asynthetic
64
+ ```
65
+
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "asynthetic": {
70
+ "command": "npx",
71
+ "args": ["-y", "asynthetic"]
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ The published package bundles the curated maps, so local stdio mode works offline with no database or configuration.
78
+
79
+ ## HTTP endpoints
80
+
81
+ When the `PORT` environment variable is set, the server runs as an HTTP service (otherwise it speaks stdio):
82
+
83
+ | Endpoint | Transport |
84
+ |---|---|
85
+ | `POST` / `GET` / `DELETE` `/mcp` | **Streamable HTTP** — the current MCP transport; use this from modern clients |
86
+ | `GET /sse` + `POST /messages` | Legacy HTTP+SSE — compatibility for older clients (protocol 2024-11-05) |
87
+ | `GET /` | Health/info JSON (name, version, active store, endpoints) |
88
+
89
+ Sessions are managed per client with `Mcp-Session-Id` (Streamable HTTP) or `sessionId` (legacy SSE); each session gets an isolated server instance.
90
+
91
+ ## Response shape
92
+
93
+ Successful lookups return structured JSON with full verification metadata:
94
+
95
+ ```jsonc
96
+ {
97
+ "found": true,
98
+ "match_type": "semver-range",
99
+ "resolved_via": "semver_range",
100
+ "match_note": "Resolved via SemVer range processing: ...",
101
+ "migration": {
102
+ "package": "next",
103
+ "from_version": "14.2.35",
104
+ "to_version": "15.0.0",
105
+ "breaking_changes": [ /* ordered, with before/after code */ ],
106
+ "deprecations": [ /* symbol, replacement, removal timeline */ ],
107
+ "source_urls": ["https://nextjs.org/docs/app/guides/upgrading/version-15"],
108
+ "last_verified": "2026-07-03",
109
+ "status": "draft"
110
+ }
111
+ }
112
+ ```
113
+
114
+ Missed lookups return `found: false`, an explicit anti-hallucination instruction, and the list of maps that do exist.
115
+
116
+ ## Self-hosting
117
+
118
+ ```sh
119
+ git clone https://github.com/asyntheticai/asynthetic.git
120
+ cd asynthetic
121
+ npm install
122
+ npm run build
123
+ ```
124
+
125
+ | Variable | Effect |
126
+ |---|---|
127
+ | *(none)* | Serves the bundled JSON maps from `data/maps/` — zero-config mode |
128
+ | `SUPABASE_URL` + `SUPABASE_ANON_KEY` | Serves from Postgres (run `schema/schema.sql`, then `npm run seed`) |
129
+ | `SUPABASE_SERVICE_ROLE_KEY` | Needed by `npm run seed` only |
130
+ | `PORT` | Switches from stdio to the HTTP transports above |
131
+ | `MIGRATION_DATA_DIR` | Overrides the local maps directory |
132
+
133
+ The server never crashes on missing configuration — absent database credentials fall back to the bundled maps with a note on stderr.
134
+
135
+ ## Development
136
+
137
+ ```sh
138
+ npm run smoke # build + 17-check end-to-end suite (stdio, Streamable HTTP, and SSE)
139
+ npm run inspect # build + launch @modelcontextprotocol/inspector against the server
140
+ npm run dev # run from source over stdio
141
+ ```
142
+
143
+ ### Adding a migration map
144
+
145
+ 1. Curate from **official sources only** (changelogs, GitHub releases, migration guides). Record every URL.
146
+ 2. Add a JSON file under `data/maps/<ecosystem>/<package>/` following the schema in `src/types/migration-map.ts`. Files are Zod-validated at load and seed time; invalid maps are skipped with a warning, never served.
147
+ 3. Set `status: "draft"` until snippets are verified against real code, then `"verified"`. Mark superseded maps `"stale"` (excluded from serving) instead of deleting them.
148
+ 4. `npm run seed` to sync Postgres, if used.
149
+
150
+ ## Project layout
151
+
152
+ ```
153
+ schema/schema.sql Postgres tables (migrations, breaking_changes, deprecations)
154
+ src/types/migration-map.ts TypeScript types + Zod validator for map JSON
155
+ src/store/ Store interface, Supabase + local-file backends, SemVer resolver
156
+ src/server.ts MCP server factory (tool registration)
157
+ src/index.ts Entry point: stdio or HTTP by environment
158
+ data/maps/ Hand-curated migration maps (source of truth)
159
+ scripts/seed.ts Load data/maps into Supabase
160
+ scripts/smoke.ts End-to-end test suite
161
+ ```
162
+
163
+ Built with TypeScript, the official [`@modelcontextprotocol/sdk`](https://www.npmjs.com/package/@modelcontextprotocol/sdk) (v1.x stable line), Zod, Express, and Supabase. Node.js 22+.
@@ -0,0 +1,249 @@
1
+ {
2
+ "ecosystem": "npm",
3
+ "package": "ai",
4
+ "from_version": "4.3.19",
5
+ "to_version": "5.0.0",
6
+ "summary": "AI SDK 5.0 (published 2025-07-31) is a ground-up redesign of the Vercel AI SDK. UI packages leave the core: ai/react -> @ai-sdk/react, ai/rsc -> @ai-sdk/rsc, and the LangChain/LlamaIndex adapters move to @ai-sdk/langchain / @ai-sdk/llamaindex. The message system splits into UIMessage (app state, content string replaced by a parts[] array) and ModelMessage (CoreMessage renamed; convertToCoreMessages -> convertToModelMessages). Tools are defined with inputSchema instead of parameters, and tool calls/results use input/output instead of args/result. Core call options rename maxTokens -> maxOutputTokens and providerMetadata -> providerOptions; maxSteps is replaced by stopWhen with stepCountIs()/hasToolCall(). Streaming switches from the custom data-stream protocol to Server-Sent Events: toDataStreamResponse -> toUIMessageStreamResponse, StreamData is removed in favor of createUIMessageStream. useChat is restructured around transports (DefaultChatTransport) and sendMessage, and no longer manages input state. Provider spec moves to LanguageModelV2 (imported from @ai-sdk/provider) with usage renamed promptTokens/completionTokens -> inputTokens/outputTokens. The implicit temperature: 0 default is removed. Requires zod >= 3.25.0 (the guide now recommends Zod 4.1.8+ for TypeScript performance). Official codemods automate much of the mechanical work: npx @ai-sdk/codemod v5 (or 'upgrade'), and Vercel ships a dedicated AI SDK 5 migration MCP server.",
7
+ "breaking_changes": [
8
+ {
9
+ "title": "Upgrade baseline: ai@5, @ai-sdk/* v2, zod >= 3.25.0",
10
+ "description": "All packages must move in lockstep: ai@5.0.0, @ai-sdk/provider@2.0.0, @ai-sdk/provider-utils@3.0.0, and every @ai-sdk/* provider/UI package to 2.0.0. zod 3.25.0+ is the documented minimum; the current guide recommends zod 4.1.8+ to avoid TypeScript performance issues.",
11
+ "category": "config-change",
12
+ "affected_symbols": ["ai", "@ai-sdk/provider", "@ai-sdk/provider-utils", "zod"],
13
+ "before_code": "// package.json\n\"ai\": \"^4.3.19\",\n\"@ai-sdk/openai\": \"^1.3.0\",\n\"zod\": \"^3.23.8\"",
14
+ "after_code": "// package.json\n\"ai\": \"^5.0.0\",\n\"@ai-sdk/openai\": \"^2.0.0\",\n\"zod\": \"^3.25.0\"\n\n// npm install ai @ai-sdk/react @ai-sdk/openai zod@3.25.0",
15
+ "migration_note": "Upgrade every @ai-sdk/* package together — mixing v1-spec providers with ai@5 fails at the provider-interface level. Run the official codemods first: npx @ai-sdk/codemod v5 (they handle most renames below).",
16
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
17
+ },
18
+ {
19
+ "title": "ai/react and ai/rsc subpaths removed — use @ai-sdk/react and @ai-sdk/rsc",
20
+ "description": "The framework UI entry points are extracted from the core package: ai/react is removed in favor of @ai-sdk/react, and ai/rsc becomes the separate @ai-sdk/rsc package.",
21
+ "category": "import-change",
22
+ "affected_symbols": ["ai/react", "ai/rsc", "useChat", "createStreamableValue"],
23
+ "before_code": "import { useChat } from 'ai/react';\nimport { createStreamableValue } from 'ai/rsc';",
24
+ "after_code": "import { useChat } from '@ai-sdk/react';\nimport { createStreamableValue } from '@ai-sdk/rsc';",
25
+ "migration_note": "Install the new packages (npm install @ai-sdk/react) and rewrite the imports. The useAssistant hook is removed entirely and has no @ai-sdk/react equivalent.",
26
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
27
+ },
28
+ {
29
+ "title": "maxTokens renamed to maxOutputTokens",
30
+ "description": "The token-limit option on core functions (generateText, streamText, and object generation) is renamed maxTokens -> maxOutputTokens.",
31
+ "category": "rename",
32
+ "affected_symbols": ["maxTokens", "generateText", "streamText"],
33
+ "before_code": "const result = await generateText({\n model: openai('gpt-4.1'),\n maxTokens: 1024,\n prompt: 'Hello!',\n});",
34
+ "after_code": "const result = await generateText({\n model: openai('gpt-4.1'),\n maxOutputTokens: 1024,\n prompt: 'Hello!',\n});",
35
+ "migration_note": "Mechanical rename everywhere; the v5 codemod handles it.",
36
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
37
+ },
38
+ {
39
+ "title": "Message types renamed: CoreMessage -> ModelMessage, Message -> UIMessage",
40
+ "description": "The message type system splits into UIMessage (application/UI state, the source of truth) and ModelMessage (what is sent to the LLM). CoreMessage -> ModelMessage, Message -> UIMessage, CreateMessage -> CreateUIMessage, convertToCoreMessages() -> convertToModelMessages().",
41
+ "category": "rename",
42
+ "affected_symbols": ["CoreMessage", "Message", "CreateMessage", "convertToCoreMessages"],
43
+ "before_code": "import { CoreMessage, Message, convertToCoreMessages } from 'ai';\n\nconst modelMessages = convertToCoreMessages(messages);",
44
+ "after_code": "import { ModelMessage, UIMessage, convertToModelMessages } from 'ai';\n\nconst modelMessages = convertToModelMessages(messages);",
45
+ "migration_note": "Persist UIMessage (never ModelMessage) as your application state, and call convertToModelMessages() at the model boundary. The deprecated CoreTool* types are also removed.",
46
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
47
+ },
48
+ {
49
+ "title": "UIMessage.content string replaced by parts[] array",
50
+ "description": "UI messages no longer have a content string. All message content lives in a typed parts[] array (text, reasoning, file, tool parts, custom data parts). The 'data' message role, the message.toolInvocations property, and the message.reasoning property are removed; reasoning parts expose part.text instead of part.reasoning.",
51
+ "category": "signature-change",
52
+ "affected_symbols": ["UIMessage.content", "UIMessage.toolInvocations", "UIMessage.reasoning", "role: 'data'"],
53
+ "before_code": "const message = {\n id: '1',\n role: 'user',\n content: 'Bonjour!',\n};\n\n// rendering\n<div>{message.content}</div>",
54
+ "after_code": "const message = {\n id: '1',\n role: 'user',\n parts: [{ type: 'text', text: 'Bonjour!' }],\n};\n\n// rendering\n{message.parts.map(part =>\n part.type === 'text' ? <div key={...}>{part.text}</div> : null\n)}",
55
+ "migration_note": "Every render path and every persistence path that touches message.content must switch to iterating parts. Custom data previously sent via the 'data' role moves to typed data parts on UI message streams. File parts rename mimeType -> mediaType and data -> url.",
56
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
57
+ },
58
+ {
59
+ "title": "Tool definitions: parameters -> inputSchema; args/result -> input/output",
60
+ "description": "tool() definitions rename parameters -> inputSchema (an optional outputSchema is added, aligning with MCP). Everywhere tool calls and results surface (fullStream parts, steps, onStepFinish), args -> input and result -> output. experimental_toToolResultContent() is replaced by toModelOutput().",
61
+ "category": "signature-change",
62
+ "affected_symbols": ["tool", "parameters", "experimental_toToolResultContent", "toolCall.args", "toolResult.result"],
63
+ "before_code": "const weatherTool = tool({\n description: 'Get the weather',\n parameters: z.object({ city: z.string() }),\n execute: async ({ city }) => ({ temperature: 20 }),\n});\n\nfor await (const part of result.fullStream) {\n if (part.type === 'tool-call') console.log(part.args);\n if (part.type === 'tool-result') console.log(part.result);\n}",
64
+ "after_code": "const weatherTool = tool({\n description: 'Get the weather',\n inputSchema: z.object({ city: z.string() }),\n execute: async ({ city }) => ({ temperature: 20 }),\n});\n\nfor await (const part of result.fullStream) {\n if (part.type === 'tool-call') console.log(part.input);\n if (part.type === 'tool-result') console.log(part.output);\n}",
65
+ "migration_note": "Rename in tool definitions AND in every consumer of tool calls/results. New dynamicTool() helper covers runtime-defined tools (input/output typed unknown; check toolCall.dynamic when mixing with static tools).",
66
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
67
+ },
68
+ {
69
+ "title": "Typed tool UI parts and renamed tool states",
70
+ "description": "The generic 'tool-invocation' UI part type is replaced by per-tool part types ('tool-getWeather') plus 'dynamic-tool'. Tool part states rename: partial-call -> input-streaming, call -> input-available, result -> output-available, and a new output-error state carries execution failures.",
71
+ "category": "rename",
72
+ "affected_symbols": ["part.type === 'tool-invocation'", "state: 'partial-call'", "state: 'call'", "state: 'result'"],
73
+ "before_code": "if (part.type === 'tool-invocation') {\n if (part.toolInvocation.state === 'result') {\n return <Weather data={part.toolInvocation.result} />;\n }\n}",
74
+ "after_code": "switch (part.type) {\n case 'tool-getWeather':\n if (part.state === 'output-available') {\n return <Weather data={part.output} />;\n }\n break;\n case 'dynamic-tool':\n // runtime-defined tools\n break;\n}",
75
+ "migration_note": "Chat render code switches from one generic case to a case per tool name. Handle the new output-error state — tool execution failures now arrive here instead of throwing (see ToolExecutionError removal).",
76
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
77
+ },
78
+ {
79
+ "title": "maxSteps replaced by stopWhen (stepCountIs, hasToolCall)",
80
+ "description": "The maxSteps numeric limit on generateText/streamText is replaced by stopWhen, which stops multi-step execution when the last step contains tool results and a condition matches. maxSteps is also removed from useChat — step control moves server-side.",
81
+ "category": "signature-change",
82
+ "affected_symbols": ["maxSteps", "stopWhen", "stepCountIs", "hasToolCall"],
83
+ "before_code": "const result = await generateText({\n model: openai('gpt-4.1'),\n messages,\n maxSteps: 5,\n});",
84
+ "after_code": "import { stepCountIs, hasToolCall } from 'ai';\n\nconst result = await generateText({\n model: openai('gpt-4.1'),\n messages,\n stopWhen: stepCountIs(5), // or [stepCountIs(10), hasToolCall('submitOrder')]\n});",
85
+ "migration_note": "stepCountIs(n) is the drop-in equivalent of maxSteps: n. Arrays stop on ANY matching condition; custom predicates receive { steps }. experimental_continueSteps is removed with no replacement. step.stepType is removed — infer from position/content.",
86
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
87
+ },
88
+ {
89
+ "title": "Stream response helpers renamed: toDataStreamResponse -> toUIMessageStreamResponse",
90
+ "description": "streamText result helpers rename to the UI-message-stream family: toDataStreamResponse() -> toUIMessageStreamResponse(), pipeDataStreamToResponse() -> pipeUIMessageStreamToResponse(). DataStreamToSSETransformStream -> JsonToSseTransformStream; the streamText getErrorMessage option becomes onError.",
91
+ "category": "rename",
92
+ "affected_symbols": ["toDataStreamResponse", "pipeDataStreamToResponse", "DataStreamToSSETransformStream", "getErrorMessage"],
93
+ "before_code": "// Next.js route handler\nconst result = streamText({ model: openai('gpt-4.1'), prompt });\nreturn result.toDataStreamResponse();\n\n// Express\nresult.pipeDataStreamToResponse(res);",
94
+ "after_code": "// Next.js route handler\nconst result = streamText({ model: openai('gpt-4.1'), prompt });\nreturn result.toUIMessageStreamResponse();\n\n// Express\nresult.pipeUIMessageStreamToResponse(res);",
95
+ "migration_note": "Mechanical rename on the server; the response now speaks the SSE-based UI message stream protocol, so the client must be on v5 useChat/transports too — upgrade both sides together.",
96
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
97
+ },
98
+ {
99
+ "title": "StreamData removed — use createUIMessageStream",
100
+ "description": "The StreamData class (streamData.append(), message annotations) is removed, along with mergeStreams, writeData, and writeMessageAnnotation. Custom data streams to the client via createUIMessageStream/createUIMessageStreamResponse with typed data parts.",
101
+ "category": "removal",
102
+ "affected_symbols": ["StreamData", "mergeStreams", "writeData", "writeMessageAnnotation"],
103
+ "before_code": "import { StreamData } from 'ai';\n\nconst streamData = new StreamData();\nstreamData.append('custom-data');\nstreamData.close();",
104
+ "after_code": "import { createUIMessageStream, createUIMessageStreamResponse } from 'ai';\n\nconst stream = createUIMessageStream({\n execute({ writer }) {\n writer.write({ type: 'data-custom', id: '1', data: 'custom-data' });\n },\n});\nreturn createUIMessageStreamResponse({ stream });",
105
+ "migration_note": "Data parts are typed ('data-<name>') and render as parts on UIMessage — message annotations have no direct equivalent; model them as data parts or message metadata.",
106
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
107
+ },
108
+ {
109
+ "title": "Streaming wire protocol is now SSE",
110
+ "description": "The custom data-stream protocol is replaced by standard Server-Sent Events (data stream protocol v2). UIMessageStreamPart is renamed UIMessageChunk.",
111
+ "category": "behavior-change",
112
+ "affected_symbols": ["UIMessageStreamPart"],
113
+ "before_code": null,
114
+ "after_code": null,
115
+ "migration_note": "Anything that parsed the v4 wire format directly — custom clients, proxies, edge middleware, non-JS consumers — must be rewritten against the SSE protocol. Debugging improves: streams are visible in standard browser dev tools.",
116
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
117
+ },
118
+ {
119
+ "title": "useChat no longer manages input state",
120
+ "description": "input, handleInputChange, and handleSubmit are removed from useChat. Applications manage their own input state and call sendMessage.",
121
+ "category": "removal",
122
+ "affected_symbols": ["useChat().input", "useChat().handleInputChange", "useChat().handleSubmit"],
123
+ "before_code": "const { messages, input, handleInputChange, handleSubmit } = useChat({\n api: '/api/chat',\n});\n\n<form onSubmit={handleSubmit}>\n <input value={input} onChange={handleInputChange} />\n</form>",
124
+ "after_code": "const [input, setInput] = useState('');\nconst { messages, sendMessage } = useChat({\n transport: new DefaultChatTransport({ api: '/api/chat' }),\n});\n\nconst handleSubmit = (e) => {\n e.preventDefault();\n sendMessage({ text: input });\n setInput('');\n};",
125
+ "migration_note": "Every chat form needs a local useState (or your own form library). sendMessage({ text }) replaces both the old submit path and append().",
126
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
127
+ },
128
+ {
129
+ "title": "useChat restructured around transports; api option and append removed",
130
+ "description": "Endpoint configuration moves from the api option to transport objects (DefaultChatTransport). append() -> sendMessage(). The body option is captured at first render and stays static — dynamic values are passed per request or via a body function. Chat sharing by id is replaced by passing a shared Chat instance. id -> chatId in requests; experimental_resume -> resumeStream.",
131
+ "category": "signature-change",
132
+ "affected_symbols": ["useChat", "append", "DefaultChatTransport", "keepLastMessageOnError", "initialMessages"],
133
+ "before_code": "const [temperature, setTemperature] = useState(0.7);\nconst { messages, append } = useChat({\n api: '/api/chat',\n initialMessages: saved,\n body: { temperature }, // updated dynamically in v4\n});\nappend({ role: 'user', content: 'Hello' });",
134
+ "after_code": "const [temperature, setTemperature] = useState(0.7);\nconst { messages, sendMessage } = useChat({\n transport: new DefaultChatTransport({ api: '/api/chat' }),\n messages: saved, // initialMessages -> messages\n});\n// dynamic values go per-request now:\nsendMessage({ text: 'Hello' }, { body: { temperature } });",
135
+ "migration_note": "The silent trap is body: in v5 it is frozen at first render. Pass request-time values through sendMessage's second argument or a body: () => ({...}) function on the transport.",
136
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
137
+ },
138
+ {
139
+ "title": "LangChainAdapter moved to @ai-sdk/langchain",
140
+ "description": "LangChainAdapter is removed from the ai package. The adapter lives in @ai-sdk/langchain and switches to the UI message stream API: toUIMessageStream() composed with createUIMessageStreamResponse().",
141
+ "category": "import-change",
142
+ "affected_symbols": ["LangChainAdapter", "LangChainAdapter.toDataStreamResponse"],
143
+ "before_code": "import { LangChainAdapter } from 'ai';\n\nconst response = LangChainAdapter.toDataStreamResponse(stream);",
144
+ "after_code": "import { toUIMessageStream } from '@ai-sdk/langchain';\nimport { createUIMessageStreamResponse } from 'ai';\n\nconst response = createUIMessageStreamResponse({\n stream: toUIMessageStream(stream),\n});",
145
+ "migration_note": "npm install @ai-sdk/langchain. The adapter is now composable: toUIMessageStream() returns a stream you pass to the standard response helpers instead of a one-shot toDataStreamResponse().",
146
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
147
+ },
148
+ {
149
+ "title": "LlamaIndexAdapter moved to @ai-sdk/llamaindex",
150
+ "description": "LlamaIndexAdapter is extracted from the ai package to @ai-sdk/llamaindex, following the same UI message stream pattern as the LangChain adapter.",
151
+ "category": "import-change",
152
+ "affected_symbols": ["LlamaIndexAdapter", "LlamaIndexAdapter.toDataStreamResponse"],
153
+ "before_code": "import { LlamaIndexAdapter } from 'ai';\n\nconst response = LlamaIndexAdapter.toDataStreamResponse(stream);",
154
+ "after_code": "import { toUIMessageStream } from '@ai-sdk/llamaindex';\nimport { createUIMessageStreamResponse } from 'ai';\n\nconst response = createUIMessageStreamResponse({\n stream: toUIMessageStream(stream),\n});",
155
+ "migration_note": "npm install @ai-sdk/llamaindex, then compose toUIMessageStream() with createUIMessageStreamResponse().",
156
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
157
+ },
158
+ {
159
+ "title": "providerMetadata (input) renamed to providerOptions",
160
+ "description": "The call-level input parameter for provider-specific options renames providerMetadata -> providerOptions on generateText/streamText and friends. The deprecated experimental_providerMetadata is removed outright. (providerMetadata remains the name on RESULTS, where providers return metadata.)",
161
+ "category": "rename",
162
+ "affected_symbols": ["providerMetadata", "experimental_providerMetadata"],
163
+ "before_code": "await generateText({\n model: openai('gpt-4.1'),\n prompt,\n providerMetadata: { openai: { store: false } },\n});",
164
+ "after_code": "await generateText({\n model: openai('gpt-4.1'),\n prompt,\n providerOptions: { openai: { store: false } },\n});",
165
+ "migration_note": "Rename only where it is an INPUT. Reading provider metadata off results keeps the providerMetadata name — do not blind-replace.",
166
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
167
+ },
168
+ {
169
+ "title": "Usage tokens renamed: promptTokens/completionTokens -> inputTokens/outputTokens",
170
+ "description": "Token usage properties rename promptTokens -> inputTokens and completionTokens -> outputTokens, with totalTokens now required. On multi-step results, result.usage now covers only the FINAL step; result.totalUsage aggregates all steps.",
171
+ "category": "rename",
172
+ "affected_symbols": ["usage.promptTokens", "usage.completionTokens", "result.usage", "result.totalUsage"],
173
+ "before_code": "const { usage } = await generateText({ model, prompt });\nconsole.log(usage.promptTokens, usage.completionTokens);\n// usage covered ALL steps in v4",
174
+ "after_code": "const { usage, totalUsage } = await generateText({ model, prompt });\nconsole.log(usage.inputTokens, usage.outputTokens, usage.totalTokens);\n// usage = final step only; totalUsage = all steps",
175
+ "migration_note": "Billing/monitoring code on multi-step (tool-calling) flows silently undercounts if it keeps reading result.usage — switch aggregate accounting to result.totalUsage.",
176
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
177
+ },
178
+ {
179
+ "title": "Reasoning properties renamed",
180
+ "description": "On generateText/streamText results: result.reasoning (string) -> result.reasoningText, and result.reasoningDetails (array) -> result.reasoning. Per step: step.reasoning -> step.reasoningText. In UI reasoning parts, part.reasoning -> part.text.",
181
+ "category": "rename",
182
+ "affected_symbols": ["result.reasoning", "result.reasoningDetails", "step.reasoning", "part.reasoning"],
183
+ "before_code": "console.log(result.reasoning); // string\nconsole.log(result.reasoningDetails); // array\nfor (const step of steps) console.log(step.reasoning);",
184
+ "after_code": "console.log(result.reasoningText); // string\nconsole.log(result.reasoning); // array (was reasoningDetails)\nfor (const step of steps) console.log(step.reasoningText);",
185
+ "migration_note": "Treacherous rename: result.reasoning still EXISTS in v5 but changed type from string to array. Type-checking catches it; string interpolation does not.",
186
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
187
+ },
188
+ {
189
+ "title": "File parts: mimeType -> mediaType, data -> url",
190
+ "description": "File message parts rename mimeType -> mediaType (IANA media type) and data -> url (data URLs or remote URLs). Transcription models likewise switch mimeType -> mediaType. File attachments become file UI parts.",
191
+ "category": "rename",
192
+ "affected_symbols": ["mimeType", "part.data"],
193
+ "before_code": "{ type: 'file', mimeType: 'image/png', data: base64Data }",
194
+ "after_code": "{ type: 'file', mediaType: 'image/png', url: 'data:image/png;base64,...' }",
195
+ "migration_note": "Applies to message parts, tool result media, and attachment handling. toModelOutput() media values use { type: 'media', mediaType, data }.",
196
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
197
+ },
198
+ {
199
+ "title": "Implicit temperature: 0 default removed",
200
+ "description": "v4 set temperature: 0 by default on model calls. v5 sends no temperature unless you specify one, so the provider's own default (often 1) applies — generation output becomes less deterministic without any code change.",
201
+ "category": "behavior-change",
202
+ "affected_symbols": ["temperature"],
203
+ "before_code": "await generateText({\n model: openai('gpt-4.1'),\n prompt,\n // Implicitly temperature: 0\n});",
204
+ "after_code": "await generateText({\n model: openai('gpt-4.1'),\n prompt,\n temperature: 0, // Must explicitly set\n});",
205
+ "migration_note": "Silent output-quality regression risk: add temperature: 0 explicitly wherever v4 behavior (deterministic-ish output) was relied on, e.g. structured extraction or tests.",
206
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
207
+ },
208
+ {
209
+ "title": "Provider spec V2: LanguageModelV2 from @ai-sdk/provider",
210
+ "description": "Custom providers and middleware target the V2 spec. LanguageModelV2 is imported from @ai-sdk/provider (not 'ai'); LanguageModelV1Middleware -> LanguageModelV2Middleware (also from @ai-sdk/provider). The deprecated experimental_wrapLanguageModel is removed, and provider re-exports are dropped from the core package.",
211
+ "category": "import-change",
212
+ "affected_symbols": ["LanguageModelV2", "LanguageModelV1Middleware", "experimental_wrapLanguageModel"],
213
+ "before_code": "import { LanguageModelV2, LanguageModelV1Middleware } from 'ai';",
214
+ "after_code": "import { LanguageModelV2, LanguageModelV2Middleware } from '@ai-sdk/provider';",
215
+ "migration_note": "Anyone implementing a custom provider or middleware must re-implement against V2 (new content-part model, usage shape with inputTokens/outputTokens/totalTokens). Community providers must be upgraded to their v2-spec releases before the app moves to ai@5.",
216
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
217
+ },
218
+ {
219
+ "title": "ToolExecutionError removed — errors surface as tool-error parts",
220
+ "description": "Tool execution failures no longer throw ToolExecutionError from generateText. They arrive as 'tool-error' content parts within steps (and the output-error tool state in UI parts), so multi-step runs continue past individual tool failures.",
221
+ "category": "removal",
222
+ "affected_symbols": ["ToolExecutionError"],
223
+ "before_code": "try {\n const result = await generateText({ model, tools, prompt });\n} catch (error) {\n if (error instanceof ToolExecutionError) {\n handle(error);\n }\n}",
224
+ "after_code": "const { steps } = await generateText({ model, tools, prompt });\nconst toolErrors = steps.flatMap(step =>\n step.content.filter(part => part.type === 'tool-error'),\n);\nfor (const err of toolErrors) handle(err);",
225
+ "migration_note": "try/catch around generateText no longer sees tool failures — code that relied on it now silently ignores them. Inspect step content (or onStepFinish) for tool-error parts.",
226
+ "source_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0"
227
+ },
228
+ {
229
+ "title": "Grab-bag removals: onResponse, useAssistant, sendExtraMessageFields, toolCallStreaming, and more",
230
+ "description": "Removed with no direct replacement or now-default behavior: the onResponse callback; the useAssistant hook; sendExtraMessageFields (UI messages now sent whole by default); keepLastMessageOnError; toolCallStreaming option (tool-call streaming is always on); StreamCallbacks.onCompletion; the getUIText helper; the cosineSimilarity() options parameter. Image models lose per-model settings — maxImagesPerCall moves to the generateImage() call and other settings to providerOptions.",
231
+ "category": "removal",
232
+ "affected_symbols": ["onResponse", "useAssistant", "sendExtraMessageFields", "toolCallStreaming", "keepLastMessageOnError", "StreamCallbacks.onCompletion", "getUIText"],
233
+ "before_code": "await generateImage({\n model: luma.image('photon-flash-1', {\n maxImagesPerCall: 5,\n pollIntervalMillis: 500,\n }),\n prompt,\n n: 10,\n});",
234
+ "after_code": "await generateImage({\n model: luma.image('photon-flash-1'),\n prompt,\n n: 10,\n maxImagesPerCall: 5,\n providerOptions: {\n luma: { pollIntervalMillis: 500 },\n },\n});",
235
+ "migration_note": "Audit for each removed symbol individually — most have pattern replacements (message metadata instead of extra fields, transports instead of onResponse) described in the migration guide.",
236
+ "source_url": "https://github.com/vercel/ai/releases/tag/ai%405.0.0"
237
+ }
238
+ ],
239
+ "deprecations": [],
240
+ "source_urls": [
241
+ "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0",
242
+ "https://github.com/vercel/ai/blob/ai%405.0.0/content/docs/08-migration-guides/26-migration-guide-5-0.mdx",
243
+ "https://github.com/vercel/ai/releases/tag/ai%405.0.0",
244
+ "https://vercel.com/blog/ai-sdk-5",
245
+ "https://ai-sdk.dev/providers/adapters/langchain"
246
+ ],
247
+ "last_verified": "2026-07-03",
248
+ "status": "draft"
249
+ }