2.3
a raised Node floor, three opt-in core additions, and a security fix on SAP Commerce Cloud
Showing every integration. Set your stack in Preferences and this page hides what isn't yours.
^20.10.0 || >=22.14.0Every published package in 2.3 requires Node ^20.10.0 || >=22.14.0. A runner still on Node 20.0 through 20.9 can't install this release at all, so check your Node version before you plan anything else. The core additions are opt-in and change nothing until you turn them on: an AI toolkit that teaches coding agents your Alokai conventions, an image optimizer for both storefronts, and a retry policy for transient platform failures. Three changes apply without you asking, and each is a security fix that needs an action from you: credential handling on SAP Commerce Cloud in 2.3.0, what the middleware writes to your logs in 2.3.1, and the content type of middleware error responses in 2.3.4. The upgrade guide linked below collects every step for the line.
Highlights
Coding agents that know your Alokai conventions
@alokai/ai-toolkit@alokai/cliCoding agents didn't know about Alokai, so the code they wrote drifted toward generic Next.js patterns instead of what the framework enforces.
The new @alokai/ai-toolkit package ships Alokai skills, agent-driven review workflows, and Alokai reference docs tagged for AI agents. The new alokai ai sync command in @alokai/cli symlinks them into each agent installed on your machine (Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot). It also rewrites a managed block in your project's AGENTS.md.
One skill reviews a diff for performance regressions in Next.js and middleware code. It flags problems such as an unnecessary "use client", a raster <img> where next/image belongs, or an N+1 platform call, and gives a file, a line, and a concrete fix for each. Nine reference skills cover federation, normalizers, custom queries, SDK module extension, URL routing, headers, the config switcher, and token refresh.
The generated Next.js storefront also follows Next.js's own AI agents guidance, so an agent gets framework context as well as Alokai context. The toolkit is versioned independently of the release.
The code your team's agents write stops drifting toward generic Next.js patterns and matches what the framework enforces. A reviewer can ask an agent to check a diff for performance regressions before the pull request opens, not after. The upgrade doesn't add the toolkit to a project created before this release.
See the step in the upgrade guide →One image optimizer instead of a hand-written loader per store
@vue-storefront/next@vue-storefront/nuxtEvery store carried its own copy of an image loader, and each copy had the same two bugs.
@vue-storefront/next adds a createImageOptimizer factory. It returns a Next.js image loader and the GET route handler for app/img-proxy/[host]/[...path]/route.ts. In @vue-storefront/nuxt, an alokai.imageOptimizer.hosts config entry does the same: it registers an @nuxt/image provider and a /img-proxy/:host/**:path server route.
The optimizer rewrites URLs on a configured media host to /img-proxy/{key}/...?width=&quality=&format=auto, so a CDN in front of the storefront can transform them. The route proxies the bytes back from the origin.
You can configure more than one host, and each host reads its own environment variable, named after its key: the commerce key reads NEXT_PUBLIC_COMMERCE_MEDIA_HOST or NUXT_PUBLIC_COMMERCE_MEDIA_HOST. A sapcc URL variant handles the ?context= parameter. Each host can set its own cacheControl and override loader / encodePath / buildUpstreamUrl.
Stores that carried their own image loader can delete it and configure hosts instead. The two bugs those copies shared are gone: a missing SAP CC context parameter now passes the URL through instead of creating a null path segment, and an upstream network error returns a deterministic 502 instead of crashing the handler.
Nothing changes until you set NEXT_PUBLIC_IMAGE_OPTIMIZER_ENABLED=true or NUXT_PUBLIC_IMAGE_OPTIMIZER_ENABLED=true. Until the flag is true, image URLs pass through untouched and the proxy route returns 404. The wiring lives in files the CLI wrote when your project was created, so a project generated before this release doesn't get it from the upgrade. Add it yourself if you adopt the optimizer.
Transient platform failures can be retried instead of surfaced
@alokai/connectWhen a platform returned an occasional 503 or dropped a connection, whoever was mid-request got a failed cart or a failed checkout.
Integration API methods in @alokai/connect accept a new retry config, set per integration in middleware.config.ts. Set retry: true for the default policy: two retries with randomized exponential backoff. To use your own policy, pass { retries, retryCondition, retryDelay }.
A retry fires on 502, 503, 504, 408, and 429 responses and on normalized network failures such as ETIMEDOUT and ECONNRESET. Retry is off by default.
With retry on, an occasional 503 or dropped connection no longer fails a cart or a checkout. You choose which integrations retry and how hard they try, so a slow write endpoint doesn't inherit the same policy as a cheap read.
Error logs name the real reason a call failed
@alokai/connectWhen a call failed, the per-request error log reduced its cause to a name and a message, so you couldn't see what actually went wrong.
The GCP structured logger in @alokai/connect writes the whole cause chain of a normalized error to the per-request error log. It no longer flattens the cause to a name and a message. Logs also carry the data and cause fields of AppError subclasses such as ValidationError and HttpError.
Secret redaction, circular-reference safety, and size bounds still apply. AppError.toJSON(), which is what the error handler returns to API clients, doesn't change, so nothing extra reaches the browser.
An incident that used to show only a bare HttpError in the log now shows the transport failure under it - the ECONNRESET, the upstream status, the response body. The on-call engineer can tell a platform outage from a normalizer bug without reproducing it locally. Normalizer failures now show their data too.
All changes by area
Every change in 2.3 has one entry here, grouped by area. The highlights, from above and from the patch sections, are here too, marked with a star. Open an area to see its changes, and open a change to read why it changed and what changed. The upgrade guide from 2.2.6 to 2.3.6 collects the migration steps for the whole line.
Alokai ConnectAction needed14 changes
added Transient platform failures can be retried instead of surfaced highlight
With retry on, an occasional 503 or dropped connection no longer fails a cart or a checkout. You choose which integrations retry and how hard they try, so a slow write endpoint doesn't inherit the same policy as a cheap read.
What changedWhy, and what changed ▸
When a platform returned an occasional 503 or dropped a connection, whoever was mid-request got a failed cart or a failed checkout.
Integration API methods take a new retry config per integration in middleware.config.ts. Use retry: true for two retries with randomized exponential backoff, or set your own retries, retryCondition, and retryDelay. It fires on 502, 503, 504, 408, and 429 and on normalized network failures such as ETIMEDOUT and ECONNRESET. It's off by default.
@alokai/connectchanged Error logs name the real reason a call failed highlight
An incident that used to show only a bare HttpError now shows the transport failure under it, so the on-call engineer can tell a platform outage from a normalizer bug without reproducing it.
What changedWhy, and what changed ▸
When a call failed, the per-request error log reduced its cause to a name and a message, so you couldn't see what actually went wrong.
The GCP structured logger writes the whole cause chain of a normalized error. Logs also carry the data and cause fields of AppError subclasses such as ValidationError and HttpError. Redaction, circular-reference safety, and size bounds still apply, and what the error handler returns to API clients doesn't change.
@alokai/connectfixed The config switcher bootstraps each store as its own instance
Services created at startup, such as an authentication token service, are configured per store. They no longer share one set of values across every store you serve.
What changedWhy, and what changed ▸
The config switcher bootstraps each configured store as its own instance. Each store gets its own init() and extendApp(), and its configuration is merged correctly.
@alokai/connectfixed integrationConfigSchema accepts a real configuration object
A multi-store middleware starts instead of failing at bootstrap.
What changedWhy, and what changed ▸
integrationConfigSchema accepts any object, not only {}. It no longer rejects the configuration the config switcher injects, so middleware bootstrap no longer breaks.
@alokai/connectfixed Custom fields on SfOrderListItem are typed correctly
Reading a custom field off an order list item type-checks without a cast.
What changedWhy, and what changed ▸
Custom fields on SfOrderListItem have correct types.
@alokai/connectperformance InferAddCustomFields resolves faster
Type-checking a project with a lot of custom fields takes less time, in your editor and in CI.
What changedWhy, and what changed ▸
TypeScript resolves the InferAddCustomFields type faster.
@alokai/connectperformance pako is lazy-loaded
Your bundle is smaller if your project doesn't turn compression on.
What changedWhy, and what changed ▸
The pako compression library is lazy-loaded. Clients that never use compression no longer bundle it.
@alokai/connectfixed Requests blocked by an open circuit breaker now return their real error highlight 2.3.1
Incidents behind an open circuit breaker name the real failure, so you can diagnose them from the logs you already collect, without a reproduction.
What changedWhy, and what changed ▸
When a circuit breaker was open, the request it blocked failed with TypeError: Cannot read properties of undefined (reading 'errorBoundary') instead of its real error. So every tripped breaker looked the same in your logs.
A request blocked by an open circuit breaker returns its proper error response. The error handler in @alokai/connect no longer throws while it builds that response.
@alokai/connectsecurity A security fix in what the middleware writes to your logs highlight Action needed 2.3.1
Upgrading closes this going forward, but it can't change logs already written. If you still retain middleware logs from before the upgrade, you have one action to take outside the repo.
step →What changedWhy, and what changed ▸
A vulnerability in what the middleware could write to your logs has been identified and fixed.
The fix is inside the middleware and needs no change to your code.
@alokai/connectsecurity Error responses from the middleware can no longer be rendered as HTML highlight Action needed 2.3.4
Nothing in your project needs to opt in. The only code that can break is code that reads the content type of these error responses and expects text/html - match on the status code instead.
What changedWhy, and what changed ▸
A vulnerability in how the middleware sent error responses has been identified and fixed.
The middleware sends 404 and 405 responses, and errors thrown out of an endpoint, as text/plain with X-Content-Type-Options: nosniff instead of text/html. Status codes and bodies stay the same.
@alokai/connectchanged A failed request now logs the reason underneath it highlight 2.3.6
An SDKError: fetch failed whose real reason is a nested SocketError/ECONNRESET logs that reason under cause, so you can tell a network fault from a backend rejection by reading the log line. This applies wherever a plain Error with a cause is logged, in middleware and storefront alike.
What changedWhy, and what changed ▸
Only AppError subclasses had their cause chain serialized, so a plain Error that carried the real reason logged without it.
The GCP structured logger serializes the full cause chain of any error that carries one. The serialization itself doesn't change. It stays circular-safe, redacts secrets, and stays bounded in depth and size.
@alokai/connectadded installConsoleBridge is exported for your own use 2.3.6
Your own code that still logs through console can join the structured log stream without changes to its call sites.
What changedWhy, and what changed ▸
installConsoleBridge(logger, options?) from @alokai/connect/logger is an opt-in helper that routes raw console.error and console.warn output through the Alokai logger. Both storefront integrations use it. It renders nested errors with their stack, strips ANSI codes, and keeps non-error arguments as details metadata. Lines that are already structured pass straight through, and if the bridge fails, output falls back to the native console.
The metadata option is merged into every entry. The call returns a handle with uninstall(). The helper uses no Node built-ins, so the logger stays browser-safe.
@alokai/connectadded Storefront logger builders are exported too 2.3.6
A custom storefront entry point can build the same logger the framework uses instead of assembling its own.
What changedWhy, and what changed ▸
createStorefrontLogger(options?) builds the standard GCP-structured logger that both storefront packages use, tagged alokai.context: "storefront". withStorefrontScope(logger, scope) wraps such a logger so every entry carries per-log detail under alokai.scope. The detail goes only there, because the rest of the reserved alokai namespace stays protected.
@alokai/connectchanged Empty log messages are skipped 2.3.6
Your log stream has fewer empty entries. Pass a real message, and put the data in the log argument or metadata.
What changedWhy, and what changed ▸
The logger in @alokai/connect skips empty and whitespace-only string messages, such as logger.info("") or logger.error(" "), instead of writing an entry with no message. This applies to every consumer: middleware, storefront, and SDK.
@alokai/connectStorefrontAction needed16 changes
added One image optimizer instead of a hand-written loader per store highlight
Stores can drop their own image loader and configure hosts instead. A missing SAP CC context parameter no longer produces a broken path, and an upstream network error no longer crashes the handler. Nothing changes until you set NEXT_PUBLIC_IMAGE_OPTIMIZER_ENABLED or NUXT_PUBLIC_IMAGE_OPTIMIZER_ENABLED to true. The wiring lives in files your project owns, so the upgrade doesn't add it.
What changedWhy, and what changed ▸
Every store carried its own copy of an image loader, and each copy had the same two bugs.
@vue-storefront/next adds a createImageOptimizer factory that returns the Next.js image loader and the proxy route's GET handler. @vue-storefront/nuxt does the same through an alokai.imageOptimizer.hosts config entry. URLs on a configured media host are rewritten so a CDN in front of the storefront can transform them. You can configure several hosts, use a sapcc URL variant, and override settings per host.
@vue-storefront/next@vue-storefront/nuxtadded A typed client-side event bus for the Next.js storefront
Analytics, personalization, and search modules can emit and subscribe to events without the core knowing who listens.
What changedWhy, and what changed ▸
createStorefrontEvents from @vue-storefront/next/client builds a typed client-side pub/sub from your own event map. It returns bound emitStorefrontEvent, subscribeStorefrontEvent, useStorefrontEvent, and StorefrontEventEmitter helpers. An inline handler passed to useStorefrontEvent doesn't re-subscribe on every render. StorefrontEventEmitter emits once per mount, so it's safe under Strict Mode on server-rendered pages.
@vue-storefront/nextadded getCurrentPath reads the request path in a Server Component
A Server Component can branch on the current URL without the page passing it down.
What changedWhy, and what changed ▸
getCurrentPath from @vue-storefront/next/server returns the current request path, pathname, and search inside an App Router Server Component. It reads them from the headers that createAlokaiMiddleware sets. It has its own server entry because it imports next/headers, which client and Pages Router bundles can't use.
@vue-storefront/nextadded Listing and search gained generic extension points
A module can add listing, search, and facet behavior without patching your components. These files belong to your project, so an upgrade doesn't deliver them. A store generated before this release gets them only when you add them by hand or install a module that writes them.
What changedWhy, and what changed ▸
Each extension point is a file under config/. When no module is installed, it falls back to native behavior.
config/listing-data-source.ts defaults to the unified searchProducts. config/search-results-slot.tsx defaults to popular searches. The default submit in config/search-form.tsx navigates to the search page.
config/module-providers.tsx holds providers that modules contribute, and config/module-root-components.tsx holds their always-mounted root components. config/facet-type-config.tsx holds facet renderers. The storefront event bus and a client-hydration provider come with them.
@vue-storefront/nextchanged The search modal has a separate, pluggable search form
A module that intercepts submit and a module that reacts as you type can plug into the same input independently. Neither one overwrites the other's component.
What changedWhy, and what changed ▸
The search modal renders a separate search-form above the search results slot. The form owns submit and reports the live query through onQueryChange. The Compass assistant overlays this form instead of the base search.tsx component.
@vue-storefront/nextfixed Runtime env vars read correctly on the first client render
Integrations that read a runtime variable early no longer fail at startup. For example, the SAP CDC module failed to load the Gigya SDK because getCdcConfig() saw no API key.
What changedWhy, and what changed ▸
env('NEXT_PUBLIC_*') returns the right value during the first client render and inside initial useEffect calls. Before, runtime env vars read undefined until shortly after hydration.
@vue-storefront/next@alokai/instrumentation-next-componentfixed Instrumentation captures the initial page view
Your traces keep the entry point of every session, which is the page view most worth having.
What changedWhy, and what changed ▸
AlokaiInstrumentation captures the initial page view and any navigation during page load. Before, page-view tracking started after hydration, so the first page view and any router navigation before it were never reported.
@alokai/instrumentation-next-componentfixed Re-order fixes across the cart and product card
A re-order that partially fails now says so instead of reporting success, and the quantities on the product card track the right items.
What changedWhy, and what changed ▸
The product card keys wantedQuantities by SKU instead of cart line item ID. Items missing from the updated cart after the add-line-items call are reported as errors, not successes.
The products hook guards against an undefined lineItems and shows unhandled mutateAsync rejections as error notifications. The local-storage hook resets to its initial value when the item is removed or storage is cleared, instead of keeping a stale value.
changed The re-order module stops shipping page-scoped providers
If you installed the module before this release, both files are still in that folder, and the module doesn't remove them. They stay behind as orphans, and your order-detail page still imports one of them.
step →What changedWhy, and what changed ▸
The module no longer ships cart-page-providers.tsx and order-details-page-providers.tsx, and no longer copies them into app/[locale]/(default)/my-account/my-orders/[id]/components/. A project generated on this release hands every translation namespace to NextIntlClientProvider in its root app/[locale]/layout.tsx, so page-scoped providers aren't needed.
security The image proxy route no longer follows upstream redirects
You have nothing to do. A media host that answers image requests with a redirect now needs its final URL configured directly.
What changedWhy, and what changed ▸
A security issue in the image optimizer proxy route was found and fixed. The route no longer follows redirects from an upstream media host. It returns an upstream 3xx as a 502 instead.
@vue-storefront/next@vue-storefront/nuxtchanged Storefront UI versions moved
The upgrade doesn't change these dependency lines in your own app manifests. A project generated before this release stays on the versions it was generated with until you bump them by hand.
What changedWhy, and what changed ▸
@storefront-ui/react is now 4.0.1, @storefront-ui/vue is 3.1.2, and @storefront-ui/nuxt is 3.3.1.
added CMS components carry their type as a CSS class
A stylesheet can target a component type directly instead of reaching for a wrapper or a data attribute.
What changedWhy, and what changed ▸
Normalized CMS components carry their component type as a CSS class in the uniqueClass field. A new createComponentTypeClass utility builds the class.
@vsf-enterprise/cms-components-utilschanged Mock CMS images are absolute URLs Action needed
The change is in the content the package returns, so every Mock CMS project gets it with the version bump, even if your code imports nothing from the package. If you kept the generated Cloudinary loader, images still render, but each one takes an extra round trip. Check any code that builds paths from the renamed const, because concatenation that worked before now produces a broken path.
step →What changedWhy, and what changed ▸
Mock CMS images were relative paths that depended on a Cloudinary loader in the storefront, so the mock content worked with only that one image pipeline.
Mock CMS content references demo images as absolute URLs served by the image host. The consts.CLOUDINARY_FOLDER_NAME export is renamed to consts.CMS_MOCK_IMAGE_BASE_URL. It now holds a full base URL, not a folder name.
@vsf-enterprise/cms-mock-apiadded Next.js SSR errors now reach your log collector as single structured entries highlight Action needed 2.3.6
Next.js SSR and RSC errors reach your log collector as single GCP JSON entries in the middleware log format, with nothing to wire and no change to your local dev output. One case needs you: if your instrumentation.ts exports its own register that never references @vue-storefront/next/instrumentation, store build fails with a fix hint instead of silently shipping unnormalized logs.
What changedWhy, and what changed ▸
Next.js prints SSR and RSC errors as multi-line dumps that GCP, Datadog, and Elasticsearch can't parse.
@vue-storefront/next adds an @vue-storefront/next/instrumentation export with a ready-to-use register hook that routes the raw console.error/warn output of Next.js through the Alokai logger. store build in @alokai/cli wires the hook for you. It changes only the composed .out output, never your source.
@vue-storefront/next@alokai/cliadded Nuxt server errors are normalized the same way, with nothing to wire highlight 2.3.6
Nuxt server errors reach your logs as the same single structured entries as on the Next.js and middleware sides, and you don't set anything up. The plugin runs in production builds only, so your local terminal output doesn't change.
What changedWhy, and what changed ▸
Nitro printed raw server console output, so a Nuxt storefront's logs didn't match the Next.js and middleware format.
The Nuxt module registers a Nitro server plugin that routes Nitro's raw server console output through the Alokai logger, unhandled SSR errors included. Nested errors render with their stack, and ANSI color codes and blank spacing lines are removed.
@vue-storefront/nuxtchanged Nuxt attaches stack traces to unexpected errors by default highlight 2.3.6
A 5xx or unexpected error from a Nuxt storefront carries the stack trace you need to diagnose it, out of the box. If you kept traces out of your production logs on purpose, the upgrade turns them on. Set includeStackTrace to false explicitly to keep them out.
What changedWhy, and what changed ▸
Nuxt logged unexpected errors without a stack trace by default, unlike the middleware and Next.js.
The logger option includeStackTrace defaults to true instead of false. 4xx client errors still never carry a trace.
@vue-storefront/nuxtCLI & toolingAction needed18 changes
added Coding agents that know your Alokai conventions highlight
Code your agents write matches what the framework enforces, and a reviewer can check a diff for performance regressions before the pull request opens. The upgrade doesn't add the toolkit to a project created before this release.
step →What changedWhy, and what changed ▸
Coding agents didn't know about Alokai, so the code they wrote drifted toward generic Next.js patterns instead of what the framework enforces.
@alokai/ai-toolkit ships Alokai skills, agent-driven review workflows, and Alokai reference docs tagged for AI agents. alokai ai sync in @alokai/cli symlinks them into the agents installed on your machine and rewrites a managed block in your AGENTS.md. The toolkit is versioned independently of the release.
@alokai/ai-toolkit@alokai/clichanged Every package now requires Node 20.10 or newer Action needed
A developer machine, container image, or CI runner on Node 20.0 through 20.9 fails the engine check on install. The upgrade stops there, instead of failing after it's done. Check your runtimes before you upgrade.
step →What changedWhy, and what changed ▸
The packages use JSON ESM imports with with { type: "json" }. The previous floor accepted Node 20 releases that don't support them.
Every package published in this release declares engines.node as ^20.10.0 || >=22.14.0.
security Pinned dependencies moved to patched releases
The upgrade brings in all of the patched releases. Nothing in your project changes.
What changedWhy, and what changed ▸
The pinned versions of defu, glob, h3, lodash-es, minimatch, path-to-regexp, rollup, and diff move to patched releases in the same major version. The patched releases fix known security vulnerabilities. The public API and runtime behavior don't change.
security sanitize-html updated for a known vulnerability
The upgrade brings in the update, and no API you use changes.
What changedWhy, and what changed ▸
The updated sanitize-html mitigates CVE-2026-44990.
security uuid patched for a known vulnerability
The upgrade brings in the patch, and no API you use changes.
What changedWhy, and what changed ▸
uuid moves to ^11.1.1, which patches CVE-2026-41907.
changed axios aligned across the packages that depend on it
The upgrade brings this in, and you have nothing to change.
What changedWhy, and what changed ▸
Every package that depends on axios asks for ^1.15.2.
added AI review joins the CLI as an optional plugin
Review comments can arrive on a pull request without anyone running anything locally. The upgrade doesn't install the plugin, because it's a CLI plugin and not a project dependency.
step →What changedWhy, and what changed ▸
@alokai/cli-plugin-review registers an ai review command. It reviews a pull request, a commit, a branch diff, the working tree, or a directory, with the code-reviewer skill from your repository.
The command posts a sticky summary comment and inline findings. It posts only findings that are new. Today it posts to GitHub.
@alokai/cli-plugin-reviewadded ai fix applies the findings a review posted
The edits that follow a review are one command instead of manual work. With --dry-run, you can read the diff before anything is committed.
What changedWhy, and what changed ▸
The same plugin adds an ai fix command that applies fixes for the findings a review posted. It fixes one finding from a comment URL, every open finding on a thread, or, with --pr, every open finding on a pull request.
The command edits the files, commits, pushes to the head branch, and replies with what changed. With --dry-run, it stops at the working tree. On GitHub, the pull request author can trigger it by commenting /fix.
@alokai/cli-plugin-reviewfixed Review findings are better targeted
A large or non-monorepo diff gets the same review quality as a small one, and the comments land on the lines they're about.
What changedWhy, and what changed ▸
In a large diff, the files that look most important no longer crowd out findings in the rest. Directory reviews anchor inline findings to the correct line. The maintainability checks apply to a project of any structure, not only a monorepo packages/ layout. Inline comments render their fix snippets correctly on any git provider.
@alokai/cli-plugin-reviewadded Integration generation can be scripted
Generating an integration can run in CI or in a test, and a project on an unreleased version is no longer blocked from generating one.
What changedWhy, and what changed ▸
integration generate accepts an --answers flag. It takes a JSON object of the template's prompt answers, keyed by prompt name, and skips the interactive prompts.
The --skip-version-check flag lets generation continue when the project's Alokai version isn't in the compatibility matrix. The command prints the mismatch warning instead of aborting.
@alokai/clifixed Generated integrations compile again
A newly generated integration compiles as it comes out, with no hand-patching first.
What changedWhy, and what changed ▸
Integrations generated from the openapi and graphql templates build against the current Alokai version. Before, a freshly generated one failed to compile and needed manual fixes. The openapi template's fetch HTTP-client option also builds with no manual setup.
@alokai/clichanged The graphql template reads its base URL from the environment
You can configure a generated integration per environment from the start, without finding and replacing a placeholder.
What changedWhy, and what changed ▸
The graphql template reads the integration's baseUrl from an <INTEGRATION>_BASE_URL environment variable. It no longer uses a hardcoded placeholder.
@alokai/clichanged The sdk-proxy template ships a working endpoint
A generated sdk-proxy integration has a working endpoint to copy from instead of one you have to finish first.
What changedWhy, and what changed ▸
The sdk-proxy template ships a working getSample endpoint that returns sample data. Before, it shipped a placeholder that didn't work.
@alokai/clifixed The version check survives an unavailable version API
A short outage of the version API no longer fails your local commands or your CI.
What changedWhy, and what changed ▸
When the Alokai version API is temporarily unavailable, the CLI version check prints a warning and skips. It no longer crashes.
@alokai/clifixed wrapWith no longer injects stray parentheses
A module that wraps JSX during installation leaves valid code behind.
What changedWhy, and what changed ▸
The wrapWith JSX visitor no longer adds stray parentheses when it wraps an element inside a parenthesized return statement.
@vsf-enterprise/file-modifierfixed Deploys honor alokai.config.json when a deployment variable is left undefined highlight 2.3.2
After you upgrade the CLI, deploys pick up the values you already have in alokai.config.json, with no workflow edit. Repository variables you defined to work around this still win.
What changedWhy, and what changed ▸
The generated workflow passed any of CLI_PROJECT_NAME, CLI_FRAMEWORK, or CLI_CLOUD_REGION you left undefined as an empty string. That empty string overrode alokai.config.json, so your deploys ran with a blank project name, framework, or region.
store deploy treats an empty CLI_PROJECT_NAME, CLI_FRAMEWORK, or CLI_CLOUD_REGION as unset, so alokai.config.json supplies the value.
@alokai/clifixed A freshly generated project installs only the integrations it actually uses highlight 2.3.3
New projects get a smaller middleware install. The upgrade doesn't change an existing project, because apps/storefront-middleware/package.json belongs to your repository. Delete @vsf-enterprise/algolia-api by hand if you don't use Algolia, and @vsf-enterprise/magento-types if you aren't on Magento, then reinstall.
What changedWhy, and what changed ▸
Every generated project had @vsf-enterprise/algolia-api and @vsf-enterprise/magento-types in its middleware dependencies, even when it didn't use Algolia or Magento.
Project generation removes both packages from a project that doesn't use them.
apps/storefront-middleware/package.jsonfixed Standalone Next.js images built on npm or pnpm boot again highlight 2.3.5
Your next deploy after the upgrade picks up the fix, with no change to your app.
What changedWhy, and what changed ▸
If you built deployments with npm or pnpm, the image crashed on startup with Cannot find module 'react-dom/server.browser', because the production install removed react-dom from it. Yarn projects weren't affected.
The deploy prepare step writes a trimmed package.json with only the dependencies a standalone Next.js app needs at runtime. That list now includes react-dom alongside next and react.
@alokai/cliCompassAction needed18 changes
added Structured-output actions accept a dynamic outputSchema
An action's output shape can depend on the request - a locale, a catalog, a customer segment - instead of being fixed at build time.
What changedWhy, and what changed ▸
outputSchema in a structured-output action accepts a dynamic definition { schema, cache? }, the same shape tools use. The schema resolves on each invocation from the request context and the workflow payload. When you supply cache.key, it's cached in Redis the same way tool schemas are. Static z.ZodType schemas work unchanged.
@alokai/compassadded Failed tool calls can be retried independently
A transient tool failure gets another attempt without switching on full reaction to every tool response.
What changedWhy, and what changed ▸
retryFailedToolCalls on an action config routes failed tool calls back to the LLM for a retry even when shouldReactToToolsResponses is off.
@alokai/compassadded Tool callbacks can return instructions alongside the response
A tool can steer how its own output is read without you rewriting the action's prompt.
What changedWhy, and what changed ▸
Tool callbacks can return { toolResponse, instructions }. The LLM gets instructions as a system message that tells it how to interpret the tool result.
@alokai/compassremoved The built-in previewProducts tool is gone from the module Action needed
The upgrade doesn't touch the tool file or config.ts in an existing installation, so your assistant keeps working. You lose previewProducts when you overwrite config.ts with the version this release ships, which is also how you pick up the new model naming. If your assistant depends on the tool, keep your copy of the tool file and put its registration back.
What changedWhy, and what changed ▸
The tool shipped with the module, so every installation carried it whether or not the assistant used it.
The module no longer ships the previewProducts tool file or the CATALOG toolkit entry that registered it.
@alokai/compasschanged Model naming splits into model and legacyModel
The same configuration works whether you run through the Alokai credentials manager or straight against the gateway.
What changedWhy, and what changed ▸
Each action config carries a model and a legacyModel. model is the dated codename forwarded to the proxy in no-credentials mode. legacyModel is the concrete model used in credentials-manager mode.
@alokai/compasschanged The default Compass configuration sets both model fields
A project on the shipped configuration needs no edit to pick up the new naming.
What changedWhy, and what changed ▸
The default Compass configuration sets model and legacyModel for every workflow action, one pair per tier.
@alokai/compasschanged A bare tier still resolves in credentials-manager mode
Your existing tier-based configuration keeps working in credentials-manager mode. A tier name isn't a model the gateway knows, though, so it's worth setting real model names before you move to no-credentials mode.
What changedWhy, and what changed ▸
A configuration that still passes a bare tier keeps working in credentials-manager mode. There, heavy, medium, and light each resolve to their configured model, and any other name falls back to the light one. In no-credentials mode, model goes to the proxy verbatim.
@alokai/compasschanged The model option is typed as a string
Your IDE no longer suggests a fixed set of names that drifts from the models the gateway exposes.
What changedWhy, and what changed ▸
The model option on defineAction is typed as string, not a literal union. Its TSDoc links to the Available Models docs page.
@alokai/compassadded Compass runs without the Alokai credentials manager
You can run Compass against the gateway you choose, with a key and URL you set, without the hosted credentials manager in the path. A half-configured setup fails at startup, not at the first request.
What changedWhy, and what changed ▸
ALOKAI_COMPASS_NO_CREDENTIALS_MANAGER=true turns off the credentials manager. Compass then reads ALOKAI_COMPASS_API_KEY and ALOKAI_COMPASS_API_URL straight from the environment and sends model requests to that URL, so you can point it at any OpenAI-compatible gateway, the Alokai one included. If you set the flag without ALOKAI_COMPASS_API_URL, the server stops at startup with a clear error.
Adding the Compass module now adds a commented ALOKAI_COMPASS_API_URL line to .env.example, set to the Alokai gateway.
@alokai/compassadded SDK storage helpers for chat history and configuration
To build on the assistant's stored state, you no longer have to reach into localStorage yourself.
What changedWhy, and what changed ▸
appendToCompactHistory adds entries to the compact history stored in localStorage. getContextMessages extracts the context entries from stored chat messages. fetchConfiguration fetches a configuration document by ID, and can target a specific version.
chatId is now optional on the storage helpers and falls back to a default.
@alokai/compassfixed Chat history survives a page reload with compact history on
A shopper who reloads the page mid-conversation keeps the conversation.
What changedWhy, and what changed ▸
invoke saves the earlier conversation together with the new messages and the assistant's response. Before, it overwrote the stored history with only the latest turn. The workflow hook passes the earlier conversation as messageHistory, and invoke sends it to the server only when compact history is off.
@alokai/compassfixed Context messages persist across turns
The assistant can recall earlier search context across turns instead of losing it at the first reload.
What changedWhy, and what changed ▸
Compact history now stores context messages correctly when it saves the conversation.
@alokai/compassperformance Product visit context carries only name and sku
Every product page view sends a smaller prompt and costs fewer tokens. The assistant still knows which product it is, so it can follow up or call a tool for more detail.
What changedWhy, and what changed ▸
The product-visit context message from ProductMessage carries only { name, sku }, not the whole product object.
@alokai/compassadded A built-in tracing UI for Compass runs
You can see why the assistant answered the way it did without reading logs and guessing at the run.
What changedWhy, and what changed ▸
In development, a tracing UI at /compass/devTools records the spans of each run: LLM calls, tool calls, and workflow nodes. It shows their timeline and parent/child tree, token usage including cached tokens, and the messages exchanged with the model. It also shows each workflow's compiled graph.
@alokai/compassfixed CompassProvider no longer swallows your translations
On a storefront with Compass installed, navigation components such as NavCartButton and AuthButton no longer show missing-translation errors.
What changedWhy, and what changed ▸
CompassProvider no longer overrides the root translation provider. Before, it blocked every translation namespace except AddToCartButton and Compass.
@alokai/compassfixed The cart page loading spinner no longer sticks
On a storefront running the assistant, the cart page finishes loading.
What changedWhy, and what changed ▸
The assistant no longer forces the cart page into a render loop that kept isLoading true.
@alokai/compassfixed autoresize-textarea bumped past a broken release range
Type-checking a storefront with the Compass module installed passes again.
What changedWhy, and what changed ▸
The Compass module's Next.js dependencies now require @frsource/autoresize-textarea ^2.0.196. Versions 2.0.192 through 2.0.195 shipped without their declaration file, so tsc --noEmit failed.
@alokai/compasschanged The MCP server no longer falls back to a Cloudinary media host Action needed
If you run the MCP server and never set SAPCC_MEDIA_HOST, set it to your media origin. Until you do, images the assistant shows resolve against the wrong origin or not at all.
What changedWhy, and what changed ▸
If you never set SAPCC_MEDIA_HOST, the Compass MCP server silently used a hardcoded Cloudinary URL as your media host.
The Compass MCP server no longer falls back to a Cloudinary URL when SAPCC_MEDIA_HOST is unset. Media URLs then come back without a host prefix.
@alokai/compassRedis1 change
added A ping method checks Redis connectivity
A health check can report whether Redis is reachable, instead of inferring it from a cache miss.
What changedWhy, and what changed ▸
A new ping method checks Redis connectivity. It returns true on PONG and false otherwise. When the check fails, it logs the connection error.
@vsf-enterprise/redis-sdkSAP Commerce CloudAction needed9 changes
security Proxied SAP Commerce responses carry only four keys Action needed
Code that destructures data, which is most code, is unaffected. Code that read a base URL, a request URL, or a header it sent from response.config now gets undefined, so take those values from your own configuration. Either way, roll over your OCC application and customer tokens as a precaution.
What changedWhy, and what changed ▸
A vulnerability in how SAP Commerce credentials were handled has been identified and fixed. This part of the fix changes what your own code receives.
createProxyApi returns only { status, statusText, data, headers } from upstream SAP Commerce calls. Before, every apiClient.api.* method returned a wider response envelope.
@vsf-enterprise/sapcc-apisecurity Three OCC endpoints stop sending the application token Action needed
A stock SAP Commerce instance keeps working. If yours requires a token on these three endpoints, restore the old token mode per method in your middleware configuration.
step →What changedWhy, and what changed ▸
A vulnerability in how SAP Commerce credentials were handled has been identified and fixed. This is the second part of that fix.
getLanguages, getCountries, and getTitles no longer attach the application bearer token. They default to TokenModes.NONE.
@vsf-enterprise/sapcc-apiadded SAP Customer Data Cloud support
If your project uses SAP CDC, you can authenticate through the integration instead of wiring it up yourself.
What changedWhy, and what changed ▸
The SAP Commerce integration supports SAP Customer Data Cloud (SAP CDC).
@vsf-enterprise/sapcc-apifixed Authentication works behind a global proxy agent
If you deploy the middleware behind a corporate egress proxy, it can authenticate against SAP Commerce.
What changedWhy, and what changed ▸
Authentication no longer fails when a global proxy agent (global.GLOBAL_AGENT) is configured.
@vsf-enterprise/sapcc-apifixed Token refresh survives a network-level failure
A network failure while refreshing a token returns an error instead of taking the request down with it.
What changedWhy, and what changed ▸
Token refresh no longer crashes when its request fails at the network level.
@vsf-enterprise/sapcc-apifixed createProxyApi types the middleware context parameter
Calling a proxied method from a custom middleware method type-checks without a cast.
What changedWhy, and what changed ▸
The return type of createProxyApi now includes the middleware context parameter in each method signature.
@vsf-enterprise/sapcc-apifixed Facet matching handles prefixed values
If your catalog prefixes its facet values, those facets render and select correctly instead of silently missing.
What changedWhy, and what changed ▸
Facet value matching handles prefixed values, such as color-black and size-10, in both the facet list and the selected facets. When a product has no base options, its variant options are used instead.
@vsf-enterprise/unified-api-sapccchanged The image normalizer prefers the superZoom format
Gallery and zoom images show at a higher quality, and you don't configure anything.
What changedWhy, and what changed ▸
The image normalizer prefers the superZoom format for gallery and primary images. When superZoom is missing, it falls back to zoom, then to product.
@vsf-enterprise/unified-api-sapccfixed Customer token endpoints fail predictably when the token cookie is gone 2.3.5
Your storefront can treat that response as a logged-out user, not as an outage.
What changedWhy, and what changed ▸
Refreshing or revoking a customer token with a missing or expired token cookie returns 401 Unauthorized. It no longer crashes with an internal server error.
@vsf-enterprise/sapcc-apiMagento 21 change
fixed A stale Magento customer token no longer wedges the session 2.3.5
A shopper whose token Magento expired without the storefront knowing can log in again instead of getting stuck.
What changedWhy, and what changed ▸
This applies when Magento expires the customer token while the vsf-customer cookie is still there. An explicit login now clears the stale token and logs in again, instead of failing with "Customer is already logged in". getCustomer and getCart fall back to a logged-out or guest state instead of surfacing authorization errors.
@vsf-enterprise/unified-api-magentoContentstack1 change
fixed LivePreview type-checks against the Contentstack SDK
Live preview on Contentstack compiles without a cast or a suppression.
What changedWhy, and what changed ▸
LivePreview types the livePreviewUrl parameter of its onLiveEdit callback as optional. The handler now type-checks against the published Contentstack SDK signature, and the parameter is no longer an implicit any.
SmartEditAction needed6 changes
changed Header and footer render through connectCmsPage Action needed
Stop rendering the navbar and footer in app/[locale]/(default)/layout.tsx, or every CMS page shows two of each. This takes five coordinated edits across your layout, your page templates, and your connect-cms-page.tsx.
What changedWhy, and what changed ▸
A page that fell back to the storefront default looked different from a page with CMS content, because only the CMS branch rendered the layout.
connectCmsPage wraps each page it handles in the default layout: navbar, main content area, and footer. A fallback page now looks the same as a CMS-managed one.
@vsf-enterprise/smartedit-apiadded Resolved CMS components can be cached in Redis
Once you set a TTL, later page loads make fewer API calls and respond faster.
What changedWhy, and what changed ▸
When you set componentCache.ttl, resolved components are cached in Redis, with an in-memory fallback. This covers components resolved from the SAP Commerce API and from resolveUnknownComponents callbacks.
@vsf-enterprise/smartedit-apichanged Page resolution logs once instead of three times
An expected 404 no longer produces three log lines, and unexpected errors still show up.
What changedWhy, and what changed ▸
Page resolution collects the errors from all of its lookup steps and logs them once, after the last step runs.
@vsf-enterprise/smartedit-apifixed A self-referencing container no longer overflows the stack
An empty container authored in SmartEdit no longer takes the page down.
What changedWhy, and what changed ▸
A container component whose reference field contains its own UID no longer crashes page normalization with a stack overflow. SAP Commerce returns one, for example, for an empty tab-paragraph container. The self-reference is dropped, and the field becomes an empty array if no other UIDs remain.
@vsf-enterprise/smartedit-apifixed The getPage error message names the search params
A failed product- or category-page lookup tells you what it looked for, instead of Failed to fetch page for path: 'undefined', which named nothing you could act on.
What changedWhy, and what changed ▸
When no path is given, the getPage error message includes searchParams. A 404 no longer writes a redundant log line.
@vsf-enterprise/smartedit-apifixed SmartEdit paragraph typography is restored
Authored rich text renders with the styling it was written against.
What changedWhy, and what changed ▸
The SmartEdit paragraph component has its heading hierarchy from h1 to h6, responsive typography, and list styles back.
@vsf-enterprise/smartedit-apiCoveo1 change
security The Coveo proxy forwards only whitelisted headers
Search requests no longer hang under load. The header list isn't configurable, so the proxy now drops any other header your project relied on it to forward, such as a tracking header or a cookie.
What changedWhy, and what changed ▸
The Coveo proxy forwards only accept, authorization, content-type, and a rewritten host. It no longer passes on every incoming header. The incoming content-length and transfer-encoding described the original body, not the payload sent upstream, so requests hung and eventually exhausted middleware memory.
@vsf-enterprise/coveo-apiCI / deployment workflowsAction needed3 changes
changed Generated workflows move to v5 GitHub Actions
The upgrade doesn't rewrite the workflow files in your repository. A project generated before this release stays on v4 and keeps the warnings until you bump the versions by hand.
What changedWhy, and what changed ▸
The generated workflows use v5 of actions/checkout, actions/setup-node, and actions/cache. This clears the Node 20 deprecation warnings.
added Each store can carry its own GitHub environment
Two stores can use different container IDs and secrets instead of sharing repository-level values. The environments have to exist first, or the jobs run with empty values.
step →What changedWhy, and what changed ▸
The generate-gtm action can append a suffix to hardcoded store IDs. The workflows it generates set environment: ${{ matrix.store_id }}, so each store can have its own GitHub environment with its own variables and secrets.
changed The deployment workflow reads the Console API URL from a variable Action needed 2.3.6
The upgrade doesn't touch your workflow file, so nothing changes until you copy the new version across. The new file has no fallback, so create the CONSOLE_API_URL repository variable before you adopt it.
What changedWhy, and what changed ▸
The workflow read the Console API URL from a secret with a hardcoded fallback, though the URL isn't secret and fits an ordinary repository variable.
The generated .github/workflows/continuous-delivery.yml reads CONSOLE_API_URL from the vars.CONSOLE_API_URL repository variable. It no longer reads a secret or falls back to a hardcoded URL.
Patches in 2.3.x
2.3.6
13 Jul 2026 · Patch · 54 of 83 packages moved
Both storefronts now write structured logs. Next.js SSR errors and Nuxt server errors come out as single entries in the middleware's format, and the logger building blocks behind them are exported for your own use. Two changes ask something of you. A custom register in your instrumentation.ts has to call Alokai's, or store build fails, and the regenerated deployment workflow reads the Console API URL from a repository variable with no built-in fallback. Nuxt also attaches stack traces to 5xx errors by default now.
Next.js SSR errors now reach your log collector as single structured entries
@vue-storefront/next@alokai/cliNext.js prints SSR and RSC errors as multi-line dumps that GCP, Datadog, and Elasticsearch can't parse.
@vue-storefront/next adds an @vue-storefront/next/instrumentation export with a ready-to-use register hook. The hook routes the raw console.error/warn output of Next.js through the Alokai logger.
store build in @alokai/cli wires the hook for you. When the composed instrumentation.ts exports no register, the build appends the re-export, and when there's no such file, it creates one. It changes only the composed .out output, never your source. A file that already references the Alokai module stays exactly as it is.
In a production Node server, the storefront logger warns once when the hook never ran.
SSR and RSC errors reach your log collector as single GCP JSON entries in the middleware log format. You have nothing to wire, and your local dev output doesn't change. One case needs you: if your instrumentation.ts exports its own register that never references @vue-storefront/next/instrumentation, that hook replaces Alokai's and can't be wired. store build then fails with a fix hint instead of silently shipping unnormalized logs.
Nuxt server errors are normalized the same way, with nothing to wire
@vue-storefront/nuxtNitro printed raw server console output, so a Nuxt storefront's logs didn't match the Next.js and middleware format.
The Nuxt module registers a Nitro server plugin that routes Nitro's raw server console output through the Alokai logger, unhandled SSR errors included. Nested errors render with their stack. ANSI color codes and blank spacing lines are removed.
A Nuxt storefront writes the same single-entry structured logs as the Next.js and middleware sides, and you don't set anything up. The plugin runs in production builds only, so your local terminal output doesn't change.
Nuxt attaches stack traces to unexpected errors by default
@vue-storefront/nuxtNuxt logged unexpected errors without a stack trace by default, unlike the middleware and Next.js.
The logger option includeStackTrace defaults to true instead of false, the same as in the middleware and Next.js. 4xx client errors still never carry a trace.
A 5xx or unexpected error from a Nuxt storefront carries the stack trace you need to diagnose it, out of the box. If you kept traces out of your production logs on purpose, for log volume or to keep internal paths out of a shared aggregator, the upgrade turns them on. Set includeStackTrace to false explicitly to keep them out.
A failed request now logs the reason underneath it
@alokai/connectOnly AppError subclasses had their cause chain serialized, so a plain Error that carried the real reason logged without it.
The GCP structured logger serializes the full cause chain of any error that carries one. The serialization itself doesn't change. It stays circular-safe, redacts secrets, and stays bounded in depth and size.
An SDKError: fetch failed whose real reason is a nested SocketError/ECONNRESET logs that reason under cause. You can tell a network fault from a backend rejection by reading the log line, without reproducing the request. This applies wherever a plain Error with a cause is logged, in middleware and storefront alike.
2.3.5
9 Jul 2026 · Patch · 7 of 83 packages moved
A deploy fix for npm and pnpm builds, plus two session-handling fixes on SAP Commerce Cloud and Magento.
Standalone Next.js images built on npm or pnpm boot again
@alokai/cliIf you built deployments with npm or pnpm, the image crashed on startup with Cannot find module 'react-dom/server.browser', because the production install removed react-dom from it. Yarn projects weren't affected.
The deploy prepare step writes a trimmed package.json with only the dependencies a standalone Next.js app needs at runtime. That list now includes react-dom alongside next and react, so the production install keeps it in the image.
The fix is entirely inside the CLI. Your next deploy after the upgrade picks it up, with no change to your app.
2.3.4
7 Jul 2026 · Patch · 53 of 83 packages moved
A security fix in how the middleware sends error responses.
Error responses from the middleware can no longer be rendered as HTML
@alokai/connectA vulnerability in how the middleware sent error responses has been identified and fixed.
The middleware sends 404 and 405 responses, and errors thrown out of an endpoint, as text/plain with X-Content-Type-Options: nosniff instead of text/html. Status codes and response bodies stay the same.
Nothing in your project needs to opt in. The only code that can break is code that reads the content type of these error responses and expects text/html - match on the status code instead.
2.3.3
6 Jul 2026 · Patch · 2 of 83 packages moved
Newly generated projects no longer install two platform packages they don't use.
A freshly generated project installs only the integrations it actually uses
apps/storefront-middleware/package.jsonEvery generated project had @vsf-enterprise/algolia-api and @vsf-enterprise/magento-types in its middleware dependencies, even when it didn't use Algolia or Magento.
Project generation removes both packages from the middleware dependencies of a project that doesn't use them.
Projects you generate from this release on have a smaller middleware install and two fewer packages to keep on a supported version. The upgrade doesn't change a project that already exists, because apps/storefront-middleware/package.json belongs to your repository. Delete @vsf-enterprise/algolia-api by hand if you don't use Algolia, and @vsf-enterprise/magento-types if you aren't on Magento, then reinstall.
2.3.2
2 Jul 2026 · Patch · 3 of 83 packages moved
A deploy fix for projects that left any of the CLI's deployment variables undefined.
Deploys honor alokai.config.json when a deployment variable is left undefined
@alokai/cliThe generated GitHub Actions workflow passed any of CLI_PROJECT_NAME, CLI_FRAMEWORK, or CLI_CLOUD_REGION you left undefined as an empty string. That empty string overrode alokai.config.json, so your deploys ran with a blank project name, framework, or region.
store deploy treats an empty CLI_PROJECT_NAME, CLI_FRAMEWORK, or CLI_CLOUD_REGION as unset, so alokai.config.json supplies the value. A non-empty value still overrides the config file.
After you upgrade the CLI, deploys pick up the values you already have in alokai.config.json, with no workflow edit. Repository variables you defined to work around this keep working, so you can leave them in place or drop them.
2.3.1
2 Jul 2026 · Patch · 53 of 83 packages moved
A security fix in what the middleware writes to your logs, and a fix that makes requests blocked by an open circuit breaker return their real error.
Requests blocked by an open circuit breaker now return their real error
@alokai/connectWhen a circuit breaker was open, the request it blocked failed with TypeError: Cannot read properties of undefined (reading 'errorBoundary') instead of its real error. So every tripped breaker looked the same in your logs, and you couldn't tell which backend call broke.
A request blocked by an open circuit breaker returns its proper error response. The error handler in @alokai/connect no longer throws while it builds that response.
Incidents behind an open circuit breaker name the real failure, so you can diagnose them from the logs you already collect, without a reproduction.
A security fix in what the middleware writes to your logs
@alokai/connectA vulnerability in what the middleware could write to your logs has been identified and fixed.
The fix is inside the middleware and needs no change to your code.
Upgrading closes this going forward, but it can't change logs already written. If you still retain middleware logs from before the upgrade, you have one action to take outside the repo.
See the step in the upgrade guide →