Alokai

2.5

Connect Admin gains workflows and sign-in, an AI migration skill upgrades a project end to end, and version report shows what a project runs

Showing every integration. Set your stack in Preferences and this page hides what isn't yours.

Minorlatest 2.5.0 · 6 Oct 2026Node 20.* || >=22.14.0

If you run the Connect Admin editor in production, read the sign-in step before you deploy: once @alokai/connect-admin is at 1.1.0, every authoring call answers 404 until a FusionAuth application is configured. If you have an OpenAPI integration generated for the axios client, annotate its createRequestFunction before the middleware builds against axios 1.20. The SmartEdit and Nuxt fixes in this release are hand edits to files your project owns - the package bump alone doesn't deliver them.

Highlights

Workflows in Connect Admin run multi-step jobs with review gates

OptionalConnect Admin@alokai/connect-admin
Why:

A feature could hold forms and list panels, but anything that had to run - refresh a curation, push a configuration, react to an import - lived outside Connect Admin, with no place for a person to approve a result before it went live.

@alokai/connect-admin 1.1.0 adds a third view kind, kind: "workflows", next to form and list-panel. A workflows view holds the workflows you declare, as a static array or a resolver the server calls per request. Each workflow is an ordered list of steps: task steps with a run function, and review steps where a run stops and waits for a person. The reviewer sees a title, a summary, the step's JSON and optionally an HTML view rendered in a sandbox. Approve and Reject come built in, and you can declare your own decisions with their own follow-on steps.

defineFeature({
  enabled: true,
  id: "merchandising",
  name: "Merchandising",
  views: [
    {
      id: "jobs",
      kind: "workflows",
      label: "Jobs",
      workflows: [
        {
          id: "refresh-curation",
          label: "Refresh curation",
          steps: [
            { id: "fetch", kind: "task", label: "Fetch candidates", run: async (ctx) => ({}) },
            { id: "approve", kind: "review", label: "Approve the list" },
            { id: "publish", kind: "task", label: "Publish", run: async (ctx) => ({}) },
          ],
        },
      ],
    },
  ],
});

A run starts in one of two ways: a person clicks Run workflow, or the workflow's webhook is called with its per-workflow secret. There's no scheduler. A webhook can carry an input mapping and a debounceMs, so a burst of deliveries becomes one run started once the deliveries stop, and the pending burst survives a server restart. A workflow marked hidden can only be started by its webhook.

Runs are parallel by default. concurrency: "single" makes a workflow refuse a second run while one is working: the button is disabled with a link to the running one, and a webhook that fires meanwhile is recorded as a skipped row so the delivery leaves a trace. A run waiting for review can be ended and replaced after confirming. Reviews expire after 24 hours, a housekeeping pass every 5 minutes ends them and any run whose server died, and a reviewer who decides on a gate that's gone is told why. Run logs are capped at 256 KB per run. The run drawer shows a stepper, each step's output and a live log console, and the run is in the URL (?wf=&run=) so a reload keeps your place.

A workflow's inputs - the box filled in before pressing Run - accepts text, textarea, number, boolean, toggle, select, multiSelect and multiSelectDropdown, typed as WorkflowInputSchema. A static workflow with another kind refuses to start at boot and names the workflow, the input and the kind; a resolver-built workflow with one is logged and left out of the list.

For you:

Nothing changes unless you declare a workflows view. When you do, put concurrency: "single" on any workflow that writes somewhere, and trigger timed work from whatever already runs on a timer in your project by calling the workflow's webhook. The bundled playground has six demo workflows and a Workflow campaigns form that drives them, so you can try the feature under dev-module with no commerce backend.

Connect Admin asks for a sign-in, and CONNECT_ADMIN_ROLE=admin marks a dedicated admin deployment

Action neededConnect Admin@alokai/connect-admin
Why:

The editor was reached through a dev gate, CONNECT_ADMIN_FEATURE_EDITOR=true, which decided whether the tool was on, not who was using it. Anyone who could reach the middleware in production could change the configuration.

The admin UI in @alokai/connect-admin 1.1.0 asks for a FusionAuth email and password. CONNECT_ADMIN_FUSIONAUTH_APPLICATION_ID names the application whose registered users may sign in, CONNECT_ADMIN_FUSIONAUTH_CLIENT_SECRET is its secret where the application needs one, and CONNECT_ADMIN_FUSIONAUTH_URL overrides the default SSO host https://sso.vuestorefront.cloud. When the application id is set, sign-in is required everywhere, your own machine included. In production with the editor enabled and no application id, every authoring route answers 404 with {"error":"Not found"} and the UI shows a "Sign-in isn't set up" screen, so the authoring surface never looks like it exists to an outsider. Locally without an application id, nothing is gated, as before. getFormValues is never behind the gate.

CONNECT_ADMIN_ROLE=admin marks a deployment that serves the full authoring surface and no getFormValues; runtime reads stay on the storefront middleware. It replaces CONNECT_ADMIN_FEATURE_EDITOR, which is still read and means "serve everything, getFormValues included". Any other value of CONNECT_ADMIN_ROLE is logged as unknown and treated as unset.

Installing the module writes Alokai's shared demo application id and secret into the middleware .env.example, so the flow works immediately. The package recognises that id: a production deployment still using it logs, once per process, that anyone registered for the demo application can sign in and change its configuration. Signing out deletes the session on the server.

For you:

If the editor is enabled on a production deployment, it stops working at 1.1.0 until you set the FusionAuth variables, so get your own application from Alokai and configure it before you deploy. If you adopted the module at an earlier release, your .env files don't have the variables at all - the installer only writes them on a fresh install. If you only run the editor locally, nothing changes until you set an application id, after which you sign in locally too.

See the step in the upgrade guide →

An AI agent skill migrates a project to a target Alokai version in one run

OptionalCLI & tooling@alokai/ai-toolkit
Why:

Upgrading across several releases meant reading each release's steps, working out which apply to your stack, applying them by hand and finding out what broke afterwards.

@alokai/ai-toolkit 1.2.0 ships the alokai-migration skill. Ask your AI agent to upgrade the project to the latest Alokai version, or name a version, and the skill works out where the project is package by package, plans the whole range from the published migration steps, applies it and validates until the project works. A project whose packages sit on different releases is migrated from the oldest of them, and packages already ahead stay untouched; nothing is ever moved backward. Each step is checked against your stack, so a SAP Commerce step is never applied to a commercetools project, and a step that depends on something outside the repository or on a decision only your team can make is handed back to you instead of guessed.

Before the first edit the skill records a baseline of lint, typecheck and the server-less tests. Afterwards it lints, typechecks, builds, boots and runs every test suite it finds, and treats a failure not in the baseline as work left: it corrects the migration and reruns, and lists every such fix as a gap in the published steps. Anything a step asks for outside the project - a credential, a secret, a disabled check - is reported, not run. The final report accounts for every step: applied, skipped with the signal that ruled it out, already done, handed back, awaiting your answer, or applied with a note that its mechanism doesn't achieve what it claims.

The same package now carries areas.json, the machine-readable Alokai stack taxonomy - every area with the package patterns and project signals that identify it, plus per-package delivery metadata. The CLI's version report reads it.

For you:

Run ./node_modules/.bin/alokai-cli ai sync to surface the skill in .claude/skills/ai-toolkit-alokai-migration and the equivalent paths for Cursor, Codex, Gemini CLI and GitHub Copilot, then ask the agent for the upgrade. Projects the CLI didn't generate are supported through their own root scripts.

version report prints what a project runs, and the version commands tell apart releases that share core pins

OptionalCLI & tooling@alokai/cli
Why:

There was no read-only way to see which Alokai packages a project runs grouped by area, and the only command that worked the release out, version check, also wrote to package.json. The CLI works the release out from the installed versions of @alokai/connect, @alokai/cli, @vue-storefront/next and @vue-storefront/nuxt, so two releases that pin the same four versions were indistinguishable and the newer one was always assumed.

version report in @alokai/cli 2.9.0 prints the installed Alokai packages grouped by area - Connect, framework, commerce, CMS, search - with the detected Alokai version and a link that opens the documentation preferences page pre-filled for the project. --json prints the project's compatibility matrix entry in the shape of the published Alokai versions, ready to paste into that page. The command never touches package.json. It needs @alokai/ai-toolkit installed and otherwise stops with Failed to build the version report: Cannot resolve @alokai/ai-toolkit/areas.json from <cwd>. Install @alokai/ai-toolkit (yarn alokai ai sync does this) and retry.

When several releases match the installed core packages, the version label in the root package.json decides. When it names none of them, the newest is assumed, version check and version report warn with Versions <a>, <b> share these core package versions; assuming <b>. Set "version" in the root package.json to the release this project is on to pin it., and version check leaves the labels untouched instead of writing the guess. When the next release changes no dependency, version upgrade moves the version labels to it and prints Moved the project version label to <version>. Run the upgrade again to continue to the next release., so the following run carries on. version check now finishes writing the labels before reporting success, and a failed write fails the command.

version check, version upgrade and version report list the packages of the project selected with --cwd or ALOKAI_CWD instead of the shell's current directory. Links to the Alokai versions page printed by the CLI now carry ?from=<current>&to=<next>.

For you:

Run ./node_modules/.bin/alokai-cli version report to see your stack, and paste its --json output into the documentation preferences page to filter the docs to it. If your root package.json version field names the release you're on, the version commands trust it from now on, so keep it current.

All changes by area

Every change in 2.5 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.4.2 to 2.5.0 collects the migration steps for the whole line.

Alokai Connect1 change

fixed A streaming middleware request that fails carries the server's status and message

Code that catches errors from a streaming call, such as the Compass assistant, can read the status and message the way it does for ordinary requests.

What changed ▸
Why

When the server answered a streaming request with an error status, the SDK read the error body as if it were the stream, so the caller got a generic stream error with no status code.

Changed

The SDK in @alokai/connect 2.5.1 fails such a request with an SdkHttpError carrying the status code and the server's message.

@alokai/connect
StorefrontAction needed4 changes

fixed CMS-authored links in Hero, Banner and Card follow the store's category and product routes

The upgrade doesn't touch your components. If your CMS content links to the base URLs on a store with different routes, copy the helper - it imports only your routes config - and call it where those three components build their links.

What changed ▸
Why

Shared demo content links to /category and /product/<slug>/<id>, the base storefront URLs. On SAP Commerce Cloud stores those pages live at /c and /p, so the hero "Order" and "Show more" buttons fell into the CMS catch-all route and returned 404.

Changed

New projects carry a resolveCmsLink helper - helpers/cms/resolve-cms-link.ts in Next.js, utils/cms/resolveCmsLink.ts in Nuxt - that maps such links onto routes.categoryPage and routes.productPage, keeps the query string, and passes every other link through unchanged. The Hero, Banner and Card components call it, and the Banner's full-surface overlay uses the localized link component so it respects the locale prefix.

performance CMS media components load the LCP image first and pass the video accessibility audits

Your files aren't changed by the upgrade. Each item is a one-attribute edit in the CMS page components, next.config.mjs and the SmartEdit banner components if you want the same Lighthouse and axe results.

What changed ▸
Changed

In new projects, above-the-fold banner images carry fetchpriority="high"; background and banner videos use preload="metadata", play inline on iOS and include a captions track; the Compass use-cases tutorial image goes through the image optimizer; the user settings button's aria-label contains its visible text; and the Next.js image deviceSizes include 1440 so viewports around 1300 to 1500 px don't jump to the 1920 file.

changed Product and list JSON-LD carry ratings, every gallery image and the item count

The upgrade doesn't change your json-ld.ts and useSeo* files. Port the hunks if you want crawlers to see ratings and the full gallery.

What changed ▸
Changed

In new projects the product JSON-LD includes aggregateRating when the product has ratings, lists every gallery image instead of the first, and omits empty description and image rather than sending empty strings. The item list JSON-LD gives each product its rating and primary image and carries numberOfItems. In Nuxt the output escapes < so content can't break out of the script tag, SeoItemListFields is exported and imageUrl accepts an array.

fixed The circular loading spinner rotates again Action needed

Your project owns packages/tailwind-config/package.json, so the upgrade doesn't move the pin and your spinner stays still until you bump it.

step →
What changed ▸
Why

Storefront UI's Tailwind 4 theme was missing the spin-slow keyframes, so SfLoaderCircular and anything using animate-spin-slow stopped rotating after the Tailwind 4 migration.

Changed

The fix ships in Storefront UI and reaches a project through @storefront-ui/react 4.0.2 or @storefront-ui/vue 3.1.3 and newer. New projects pin 4.0.3 and 3.1.4 in packages/tailwind-config/package.json, and the Next.js app pins @storefront-ui/react 4.0.3.

CLI & toolingAction needed6 changes

added An AI agent skill migrates a project to a target Alokai version in one run highlight

Run ./node_modules/.bin/alokai-cli ai sync to surface the skill in .claude/skills/ai-toolkit-alokai-migration and the equivalent paths for Cursor, Codex, Gemini CLI and GitHub Copilot, then ask the agent for the upgrade. Projects the CLI didn't generate are supported through their own root scripts.

What changed ▸
Why

Upgrading across several releases meant reading each release's steps, working out which apply to your stack, applying them by hand and finding out what broke afterwards.

Changed

@alokai/ai-toolkit 1.2.0 ships the alokai-migration skill. Ask your AI agent to upgrade the project to the latest Alokai version, or name a version, and the skill works out where the project is package by package, plans the whole range from the published migration steps, applies it and validates until the project works. Packages already ahead stay untouched; nothing is ever moved backward. Each step is checked against your stack, and a step that depends on something outside the repository or on a decision only your team can make is handed back to you instead of guessed. Before the first edit the skill records a baseline of lint, typecheck and the server-less tests, and afterwards treats a failure not in the baseline as work left: it corrects the migration and reruns. Anything a step asks for outside the project - a credential, a secret, a disabled check - is reported, not run. The same package now carries areas.json, the machine-readable Alokai stack taxonomy, which the CLI's version report reads.

@alokai/ai-toolkit

added version report prints what a project runs, and the version commands tell apart releases that share core pins highlight

Run ./node_modules/.bin/alokai-cli version report to see your stack, and paste its --json output into the documentation preferences page to filter the docs to it. If your root package.json version field names the release you're on, the version commands trust it from now on, so keep it current.

What changed ▸
Why

There was no read-only way to see which Alokai packages a project runs grouped by area, and the only command that worked the release out, version check, also wrote to package.json. The CLI works the release out from the installed versions of @alokai/connect, @alokai/cli, @vue-storefront/next and @vue-storefront/nuxt, so two releases that pin the same four versions were indistinguishable and the newer one was always assumed.

Changed

version report in @alokai/cli 2.9.0 prints the installed Alokai packages grouped by area - Connect, framework, commerce, CMS, search - with the detected Alokai version and a link that opens the documentation preferences page pre-filled for the project. --json prints the project's compatibility matrix entry in the shape of the published Alokai versions. The command never touches package.json. It needs @alokai/ai-toolkit installed and otherwise stops. When several releases match the installed core packages, the version label in the root package.json decides. When it names none of them, the newest is assumed, version check and version report warn with Versions <a>, <b> share these core package versions; assuming <b>. Set "version" in the root package.json to the release this project is on to pin it., and version check leaves the labels untouched instead of writing the guess. When the next release changes no dependency, version upgrade moves the version labels to it and prints Moved the project version label to <version>. Run the upgrade again to continue to the next release., so the following run carries on. version check, version upgrade and version report list the packages of the project selected with --cwd or ALOKAI_CWD instead of the shell's current directory, and links to the Alokai versions page printed by the CLI now carry ?from=<current>&to=<next>.

@alokai/cli

changed store lint runs your eslint directly and prints one line per store

Your CI logs name the store that's still running or that failed. Pass --silent to keep a multistore lint log short.

What changed ▸
Why

Linting several stores in parallel shared one spinner, so a CI log couldn't show which store had started or finished. Going through npx printed npm warn Unknown env config lines on every run and could stall on an interactive prompt.

Changed

store lint in @alokai/cli 2.9.0 runs node_modules/.bin/eslint from the project root and falls back to the package-manager runner only when the binary isn't there. Progress is one plain line per store and app: Running eslint in <store> - <app>..., then ✓ <store> - <app> completed or ✗ <store> - <app> failed. alokai lint and alokai store lint accept --silent, which drops eslint's output for stores that pass; progress lines, failures with their output and the summary still print.

@alokai/cli

fixed The axios OpenAPI client needs a return type on createRequestFunction from axios 1.19 Action needed

After the upgrade your lockfile resolves axios 1.20.0 or newer, so an OpenAPI integration you generated with the axios client stops compiling until you add the two annotations to its common.ts. Don't pin axios back below 1.19 to avoid the edit - that keeps the advisory open. Redo the edit after every integration generate, because the generated file comes back without it.

step →
What changed ▸
Why

axios 1.19.0 added a non-exported unique symbol to its type declarations. A client generated by the OpenAPI generator for the axios HTTP client infers the return type of createRequestFunction from axios.request, and with 1.19 or newer that inference reaches the symbol, so tsc fails with TS2527: The inferred type of 'createRequestFunction' references an inaccessible 'unique symbol' type and the middleware doesn't build.

Changed

@vsf-enterprise/sap-commerce-webservices-sdk 7.0.4 annotates the return type of its own generated client and moves to axios 1.20.0, which also closes the HTTP/2 unhandled-error advisory GHSA-542g-h47m-68v8. The lockfile shipped with new projects resolves axios 1.20.0. The integration generator itself still emits the unannotated client.

@vsf-enterprise/sap-commerce-webservices-sdk

added someSibling matches a JSX element by what sits next to it

If you write module installers, you can check whether an element is already a sibling of {children} before inserting it, so an installer composes with modules installed earlier instead of overwriting the file.

What changed ▸
Changed

jsxElementMatches in @vsf-enterprise/file-modifier 4.1.0, and the elementMatches helper passed to createUpdateJsxVisitor insertion visitors, accept a someSibling selector. Like someChild, it takes a nested element selector and matches when at least one other child of the same parent element or fragment matches it. It works on any JSX child, including a {children} expression container.

@vsf-enterprise/file-modifier

changed The AI review plugin moves to 0.3.1 only when you install it

Installing 0.3.1 keeps your plugin on the version this release pins, but you lose nothing by staying on 0.3.0. The upgrade doesn't move it, because it lives in the CLI's plugin store, not in package.json.

step →
What changed ▸
Changed

@alokai/cli-plugin-review 0.3.1 is rebuilt against this release's CLI internals and dependencies and behaves exactly like 0.3.0.

@alokai/cli-plugin-review
CompassAction needed8 changes

fixed A cached tool schema validates calls the same way as the first call Action needed

If your root package.json still carries the zod resolution an earlier module install wrote, yarn keeps the package on that older zod, the rebuild throws, and every cache hit logs a failure and falls back to the schema function - validation is right but the cache does nothing. Move the resolution to 4.2.1.

step →
What changed ▸
Why

A tool with a cache.key rebuilt no zod schema on a cache hit, so a field declared .optional().default(x) became required and every call that omitted it failed with "Received tool input did not match expected schema".

Changed

@alokai/compass 7.1.0 rebuilds the schema from the cached JSON schema with z.fromJSONSchema, so defaults are filled in and descriptions reach the model as on the first call. That function first exists in zod 4.2.1, and the package moves to it. The compass module installer now pins the project's resolutions.zod to 4.2.1.

@alokai/compass

changed ALOKAI_COMPASS_API_URL defaults to the Alokai LLM gateway

You can drop the variable from your environment if it names the gateway. A value you set still wins.

What changed ▸
Why

In no-credentials-manager mode the module refused to start without ALOKAI_COMPASS_API_URL, although nearly every project pointed it at the same gateway.

Changed

@alokai/compass 7.1.0 defaults ALOKAI_COMPASS_API_URL to https://llm-gateway.alokai.cloud for the module and for the eval judge. ALOKAI_COMPASS_API_KEY is still required.

@alokai/compass

added Workflow and action prompts can be functions of the request

Assemble a prompt from configuration per request where you need it. The shipped chatbot workflow in the compass module files does this to append the search-assistant instructions from Connect Admin.

What changed ▸
Why

A prompt was a string fixed at middleware start, so instructions authored elsewhere - in a Connect Admin form, behind a feature flag - needed a restart to reach the model.

Changed

prompt on an action and workflowPrompt on a workflow in @alokai/compass 7.1.0 accept (context) => string | Promise<string>. The function is resolved on every invocation. String prompts are unchanged.

@alokai/compass

added Eval variants select a middleware configuration by configId

Nothing changes until you declare entries. If you adopted the module before this release, the switcher file and the turbo.json entries are hand edits, because the upgrade doesn't rewrite module files you own.

step →
What changed ▸
Why

Comparing two prompts, models or toolsets in an eval meant starting the store twice with different code.

Changed

The compass module files register a config switcher extension, built on @alokai/connect/config-switcher, on the Compass integration. Entries live in extensions/configSwitcher/index.ts, so they can hold functions such as a prompt assembled per request; entries that hold only JSON can also come from ALOKAI_COMPASS_CONFIG_SWITCHER_PROFILES as a JSON map of id to configuration. A variant in eval-config.json names an entry with configId; the runner passes it as ALOKAI_COMPASS_CONFIG_ID, the store serves that entry to requests without the x-alokai-middleware-config-id header, the evals send the id as the header, and the report meta records configId. A store started with an id that names no entry fails to start. With no entries the switcher does nothing.

In @alokai/compass 7.1.0 compiled workflows are cached per integration instance rather than per workflow id, so each switcher entry runs its own prompts, models and tools instead of whichever instance compiled the workflow first. Dev tracing is set up once per process although init runs for every instance.

The module installer adds ALOKAI_COMPASS_CONFIG_ID and ALOKAI_COMPASS_CONFIG_SWITCHER_PROFILES to globalPassThroughEnv in the project's turbo.json; store dev runs through turbo, whose strict env mode would otherwise drop them before they reach the middleware.

@alokai/compass

added Evals use matchers, declared datasets and a simulated shopper

The compass module files carry a Vitest setup and evals config that register the matchers, plus the datasets. Evals in a project that adopted the module before this release were written against helpers that no longer exist; take this release's module files to run them.

What changed ▸
Why

The eval helpers that shipped during this cycle asserted through their own functions, loaded queries from a JSON corpus, judged with a pinned temperature, and lost a scenario the runner killed on timeout.

Changed

@alokai/compass 7.1.0 exports evalMatchers() for the runner's expect: expect(text).toPassLlmJudge(criteria, { scale, threshold }) and expect(toolCalls).toCallTools(expected), where each expected call may carry args checked as a subset of the real arguments. scenario() and scenarios() repeat a body runs times and gate on minPassRate and maxInvocationMs; the context carries instrumentedInvoke and sdk. defineDataset(entries, schema?) declares a data-driven eval's entries in a .ts file, validated at import. createShopper({ goal, persona?, rounds }) plays a shopper that writes each user turn from the transcript, and talkTo(chat) runs the conversation through the middleware or, in Playwright, through the storefront widget. compactHistory and onCompactHistory on the SDK's invoke let callers without browser storage carry the conversation. createTextMessage, createContextMessage, getMessagesText and getConversationText build and read messages.

The judge can be configured in code - createEvals({ judge: { model } }), per scenario, or judgeWith(settings) - and speaks the OpenAI Responses API by default; EVAL_JUDGE_API=chat-completions selects the other, and no temperature is sent unless EVAL_JUDGE_TEMPERATURE is set. A 400 with a pinned temperature is diagnosed, not assumed. A scenario the runner kills appears in the report as a failed entry with the reason. evals run and evals report print a plain-text table per variant and a scenario-by-variant grid, rendered by formatTerminalSummary. The shipped search dataset names only products the catalog stocks, one intent per query, and EVAL_RUNS applies to it. yarn test:evals runs Vitest with a 180 second per-test timeout.

@alokai/compass

fixed The shopping assistant stops repeating widget contents and filters brands as facets

Replies get shorter and brand queries return products. The brand facet, the feature read and the probe live in module files, which the upgrade doesn't rewrite; re-running the module install brings them in and overwrites edits you made under sf-modules/compass.

What changed ▸
Why

After searchProducts or getCart rendered a widget, the model received the full payload and listed it again as text. A brand named in a query was searched as text, which the SAP text search doesn't index, so "Burton" returned nothing and "Vans" matched "Pant". The ready probe posted an empty invoke, which logged "Workflow undefined not found" on every store start.

Changed

In @alokai/compass 7.1.0 the model receives a compact reference table after a widget renders - name, ids, SKU, attributes, prices, short description - with instructions not to re-list what the user sees; silent tool calls get the same table, cart mutation tools carry the same instruction, a search with no products pushes no empty widget and tells the model how to recover, every search result carries page X of Y, and the trimmed cart summary formats addresses, shipping method and coupons as text with line item attributes and counts. In the module files, searchProducts exposes the catalog's brand facet as facets.brand and its description steers brands and generic words away from free text, the shopping assistant reads the search-assistant Connect Admin feature per request - extra instructions and a default page size, with two shipped versions default and facets-first - and falls back to code defaults when the feature or Connect Admin isn't there, and the ready probe asks /compass/inspect for the chatbot workflow.

@alokai/compass

changed Compass analytics can authenticate with Application Default Credentials

A deployment with all three variables behaves as before. One with only the database id, which was off before, now dispatches through the runtime's default credentials and logs a dispatch error where there are none.

What changed ▸
Why

Analytics dispatch required an explicit client email and private key even where the runtime already had Google credentials, and a failed dispatch logged a full stack trace.

Changed

In @alokai/compass 7.1.0 analytics is on when ALOKAI_CLOUD_ANALYTICS_DATABASE_ID is set. Explicit credentials are used when ALOKAI_CLOUD_ANALYTICS_CLIENT_EMAIL and ALOKAI_CLOUD_ANALYTICS_PRIVATE_KEY are both set, otherwise Application Default Credentials. ALOKAI_CLOUD_ANALYTICS_PROJECT_ID overrides the project. A failed dispatch logs one line without a stack trace.

@alokai/compass

changed The Compass CLI plugin moves to 1.2.0 only when you install it

If you run Compass evals, install 1.2.0 to use storeId and configId variants. If you adopted the module, the installer appended an unpinned plugins install @alokai/cli-plugin-compass to your root postinstall, which reinstalls the newest published plugin on every yarn install.

step →
What changed ▸
Why

The plugin lives in the CLI's own plugin store, not in package.json, so neither the upgrade command nor a dependency install moves it.

Changed

@alokai/cli-plugin-compass 1.2.0 lets a variant in eval-config.json name a multistore store with storeId; the runner then starts that store with store dev --store-id <id>, {storeId} substitutes in store.start, store.healthcheckUrls, store.readyCommand and test.env, and EVAL_STORE_ID reaches the suite and the report meta. A judge block at the root and per variant (model, baseUrl, apiKey, api, temperature) becomes the EVAL_JUDGE_* environment of the test process. evals run verifies every connectAdminVersions id of a variant against the started store before its tests and fails the variant naming the missing ids. A variable set in the shell wins over the same key in test.env, as documented.

@alokai/cli-plugin-compass
Connect AdminAction needed8 changes

added Workflows in Connect Admin run multi-step jobs with review gates highlight

Nothing changes unless you declare a workflows view. When you do, put concurrency: "single" on any workflow that writes somewhere, and trigger timed work from whatever already runs on a timer in your project by calling the workflow's webhook. The bundled playground has six demo workflows and a Workflow campaigns form that drives them under dev-module.

What changed ▸
Why

A feature could hold forms and list panels, but anything that had to run - refresh a curation, push a configuration, react to an import - lived outside Connect Admin, with no place for a person to approve a result before it went live.

Changed

@alokai/connect-admin 1.1.0 adds a third view kind, kind: "workflows", next to form and list-panel. Each workflow is an ordered list of steps: task steps with a run function, and review steps where a run stops and waits for a person. Approve and Reject come built in, and you can declare your own decisions with their own follow-on steps. A run starts when a person clicks Run workflow or the workflow's webhook is called with its per-workflow secret. There's no scheduler. A webhook can carry an input mapping and a debounceMs, so a burst of deliveries becomes one run. Runs are parallel by default; concurrency: "single" makes a workflow refuse a second run while one is working, and a webhook that fires meanwhile is recorded as a skipped row. Reviews expire after 24 hours, run logs are capped at 256 KB per run, and the run is in the URL (?wf=&run=) so a reload keeps your place. A workflow's inputs accepts text, textarea, number, boolean, toggle, select, multiSelect and multiSelectDropdown, typed as WorkflowInputSchema.

@alokai/connect-admin

changed Connect Admin asks for a sign-in, and CONNECT_ADMIN_ROLE=admin marks a dedicated admin deployment highlight Action needed

If the editor is enabled on a production deployment, it stops working at 1.1.0 until you set the FusionAuth variables, so get your own application from Alokai and configure it before you deploy. If you adopted the module at an earlier release, your .env files don't have the variables at all. If you only run the editor locally, nothing changes until you set an application id.

step →
What changed ▸
Why

The editor was reached through a dev gate, CONNECT_ADMIN_FEATURE_EDITOR=true, which decided whether the tool was on, not who was using it. Anyone who could reach the middleware in production could change the configuration.

Changed

The admin UI in @alokai/connect-admin 1.1.0 asks for a FusionAuth email and password. CONNECT_ADMIN_FUSIONAUTH_APPLICATION_ID names the application whose registered users may sign in, CONNECT_ADMIN_FUSIONAUTH_CLIENT_SECRET is its secret where the application needs one, and CONNECT_ADMIN_FUSIONAUTH_URL overrides the default SSO host https://sso.vuestorefront.cloud. When the application id is set, sign-in is required everywhere, your own machine included. In production with the editor enabled and no application id, every authoring route answers 404 with {"error":"Not found"} and the UI shows a "Sign-in isn't set up" screen. Locally without an application id, nothing is gated, as before. getFormValues is never behind the gate. CONNECT_ADMIN_ROLE=admin marks a deployment that serves the full authoring surface and no getFormValues; it replaces CONNECT_ADMIN_FEATURE_EDITOR, which is still read and means "serve everything, getFormValues included", and any other value of CONNECT_ADMIN_ROLE is logged as unknown and treated as unset. Installing the module writes Alokai's shared demo application id and secret into the middleware .env.example, and a production deployment still using that id logs, once per process, that anyone registered for the demo application can sign in. Signing out deletes the session on the server.

@alokai/connect-admin

added Connect Admin keeps its data in Firestore when you configure it

Configure Firestore for every production deployment, provision the TTL policies, and expect the banner until you do. On a laptop you need no GCP account. Note that during a Firestore outage a run is no longer readable from a cached copy; the copy was removed so a signed-out session can't be served from memory.

step →
What changed ▸
Why

Redis in Alokai Cloud is a cache. A flush or a re-provision emptied it, and with it the configuration versions a merchandising team had authored, with no warning anywhere.

Changed

With CONNECT_ADMIN_FIRESTORE_PROJECT_ID, CONNECT_ADMIN_FIRESTORE_CLIENT_EMAIL and CONNECT_ADMIN_FIRESTORE_PRIVATE_KEY set, @alokai/connect-admin 1.1.0 keeps everything in Firestore: config versions, workflow runs finished and running, webhook bursts, sign-in sessions and locks. A run URL keeps working after a middleware rebuild. The data already in Redis is carried over on the first read after Firestore is switched on; there's no migration script. A Firestore failure fails the save or the read loudly rather than quietly landing in the cache, with one exception: archiving a run that just finished never blocks the run, and raises the "changes are not being saved" banner instead. CONNECT_ADMIN_FIRESTORE_DATABASE_ID, CONNECT_ADMIN_FIRESTORE_USE_REST and CONNECT_ADMIN_FIRESTORE_COLLECTION_PREFIX are optional; the prefix keeps each user's data in its own namespace on a shared dev project, and scripts/cleanup-firestore.mjs deletes a namespace by hand.

Without the variables nothing changes: Redis holds the data and the process's own memory holds the locks, which is the intended way to develop. A production deployment without them says so twice - a warning in the log the first time someone opens the editor, and a banner in the UI that can't be dismissed. It isn't blocked.

seedBundle on the connect-admin config, or CONNECT_ADMIN_SEED_FILE outside production, starts a store from a configuration bundle and keeps its version ids. A read that can't reach the store throws instead of answering "nothing there", so a blip can't reseed over a customer's versions.

@alokai/connect-admin

changed Every form view keeps its own version history, and the open version belongs to your browser Action needed

Saved versions, labels and the live pointer survive the upgrade. If your middleware calls getFeatureVersions or reads currentId directly, the typecheck fails until you pass the view reference and drop the cursor. Evals that pin a version should set initialVersions[].id.

step →
What changed ▸
Why

A feature with several forms kept one version list for all of them, so saving in one form moved the version pill, the live pointer and the working draft of every other. Which version a form had open was a field on the server record, so opening a version moved every colleague's editor to it and a save landed in whichever version the last person had navigated to. Opening a second form could also carry the first form's values across and auto-save them over the second form's configuration.

Changed

In @alokai/connect-admin 1.1.0 each form view versions, publishes and previews on its own. The first time a form is opened it takes its share of the old shared list - labels, order and which version is live carry over, with only that form's schema keys in each version - and the live version's authored data is kept rather than reset to file defaults. defineFeature is unchanged: initialVersions stays on the feature and each entry is split by the keys each form owns.

Opening a version is local to your browser and makes no server call; a reload lands you where you were. Every server action names its version, so two people editing different versions of the same form no longer route each other's saves astray, and a save aimed at a version somebody removed meanwhile comes back as a fresh version holding your data. Drafts are gone: a version is named once at creation (New version, New version 2), nothing renames it automatically, names are unique case-insensitively, nothing is deleted implicitly, and a new version can be set live at once. Versions written earlier with the retired draft status are read as ordinary saved versions. New version ids are random; initialVersions[].id pins a seeded version's id so evals and scripts can name one that survives a restart, and every version card shows its id with Copy version id.

Download all in the sidebar saves every form view's history and live pointer as one bundle, and Upload reads one back through a plan shown before anything is written. Import is additive: every version in the file becomes a new saved version, nothing is overwritten or removed, no live version moves, and the browser receives a backup of the previous state. A file without a formatVersion, or from a newer Connect Admin, is refused with the reason. applyConfigImport and planConfigImport are exported for code with a request context.

"Live only for me" previews now apply on deployed storefronts, per form, and everyone who hasn't chosen a preview still gets live. Where the editor is enabled, a runtime read that names a version which no longer exists fails with VersionNotFoundError, so an eval never quietly measures the wrong version; a deployed storefront shows the live version instead and logs a warning. The version drawer flags a preview of a version that's gone and offers to stop it.

Three exports change shape: VersionStatus no longer includes "draft", FeatureVersions has no currentId, and getFeatureVersions takes a { featureId, viewId } reference instead of a feature id.

@alokai/connect-admin

added Sentence rules, dependent dropdowns, ordered pick lists and server-only views

Existing fields behave as before; stringList without options is still free text and tagList is unchanged. Put settings visitors must never see, such as internal merchandising rules, in a serverOnly view and read them from a custom middleware endpoint. A public view's templateVariables callback can read a server-only view, and whatever it puts in the public text is visible.

What changed ▸
Why

A rule an operator reads as one sentence, a dropdown whose list depends on another field, and an ordered list restricted to known values had no field kind that fit, and every form view's values were reachable from the public getFormValues route.

Changed

@alokai/connect-admin 1.1.0 adds the sentenceRule field kind: a sentence is an ordered list of segments - words, optionsInput, textInput, numberInput and repeat - and the value follows the declaration, a standalone chip under its own name and a repeat segment as one record per copy. dependsOn on a select, multiSelect, multiSelectDropdown or stringList hands the named fields' current values to the options callback as dependsOnValues; $key names the map row's key, attribute a field next to this one, /attribute a field from the form's top. options on stringList turns the add row into a picker that stays open and appends, keeps the value an ordered string[], and stops rows taking text. maxOptionsPerGroup caps how many options a group shows before the rest are found by searching; without it every option is listed in a scrolling box. A multi-value picker with more than ten options gets Show selected once something is picked.

A form view marked serverOnly: true returns null from the public /connect-admin/getFormValues route, as if it didn't exist, and logs a warning for each blocked request; your middleware still reads it through context.getApiClient("connect-admin"). sdk.connectAdmin.getFormValues always goes through the public route, from Next.js server components too, so a server-only view is read in the middleware.

A value no longer in a dropdown's list is shown as unavailable in amber, listed first and ticked, and unticking it removes it; nothing is removed on the operator's behalf, and while a list is loading, fails or comes back empty nothing is marked. A sentence-rule chip is always one line, with more than three values named as the first three and a count.

@alokai/connect-admin

changed A view's prepare data refreshes instead of living for the whole process

Opening a view now costs the prepare fetch every time, so keep prepare for data that's expensive and stable across a sitting and put anything that changes during a working day in the field's own options callback. A custom switchStrategy that resolves the store from something the editor can't see, such as a cookie, makes those stores share a cache entry; attach the config switcher to the connectAdmin integration too and its stamped id is used.

What changed ▸
Why

prepare ran once per process, so a category added in the commerce backend or a new PIM attribute reached the editor only after a middleware restart. A storefront replica, which has no authoring endpoints, could serve the same stale data to every templateVariables read until the next deploy, and with the config switcher on, whichever store asked first had its copy handed to every other store.

Changed

In @alokai/connect-admin 1.1.0 the admin app refills prepare when the operator opens or reloads a view, through the new devFeatureEditorRefreshPrepare endpoint; a view held open doesn't refetch. On a storefront replica a runtime read refills a copy older than a minute behind the request, never in front of it; CONNECT_ADMIN_PREPARE_MAX_AGE_MS changes the interval, with a 5 second floor. The cache is keyed by store, from the id the config switcher stamps on the resolved config or, failing that, the same header and domain signals its built-in strategies read. prepare keeps its signature and is still cached for one view load.

@alokai/connect-admin

added Installing the Connect Admin module registers the storefront SDK module

A project that installs the module at this release calls sdk.connectAdmin.getFormValues(...) with no manual wiring. If you installed it earlier, you wired this yourself and nothing changes.

What changed ▸
Changed

The module installer writes sdk/modules/connect-admin.ts in Next.js and sdk-modules/connect-admin.ts in Nuxt, with a connectAdmin middleware module pointed at /connect-admin, and exports it from the SDK modules barrel.

added store deploy deploys Connect Admin as its own application

Installing the module is enough; no workflow or configuration change is needed. The admin app serves the full authoring surface and no getFormValues, so runtime reads stay on your storefront middleware.

What changed ▸
Why

A dedicated Connect Admin deployment had to be set up in the Alokai Console by hand, with the right role and health check.

Changed

When the storefront middleware's package.json lists @alokai/connect-admin under dependencies, store deploy in @alokai/cli 2.9.0 sends one extra app to the Alokai Console: the same middleware image and tag, served under /connect/ with CONNECT_ADMIN_ROLE=admin and a /healthz health check.

@alokai/cli
Next.js3 changes

added Server errors are logged with their route the moment Next.js captures them

Add the re-export to your apps/storefront-unified-nextjs/instrumentation.ts to get every SSR error logged with its route in every environment.

step →
What changed ▸
Why

The console bridge installed by register only saw an error once Next.js printed it, so a 500 in a preview deployment or in next dev could go unlogged or arrive without its route.

Changed

@vue-storefront/next 10.1.0 exports an onRequestError instrumentation hook from @vue-storefront/next/instrumentation. Next.js calls it when the server captures a render, route-handler, server-action or proxy error, and the hook emits one structured ERROR entry through the storefront logger with the request path and method, the failing routePath, routeType, renderSource and the error digest under alokai.scope with source: "ssr". In production the console bridge skips errors the hook already reported, so each error still produces one entry. New projects re-export both hooks from instrumentation.ts.

@vue-storefront/next

fixed The Next.js Tailwind entry no longer declares an @source path above the project root

On the next version this release pins nothing fails. Delete the longer path now so a later move to Next.js 16.3 doesn't break your build, and check per-store overrides of the file too.

step →
What changed ▸
Why

app/tailwind.scss declared two @source paths for @storefront-ui/react, and the longer one resolved above the project root. Next.js 16.2 silently ignores it; Next.js 16.3 aborts next build with TurbopackInternalError: ... leaves the filesystem root while processing app/[locale]/globals.scss whenever the project sits inside another repository - a nested CI checkout, a monorepo subdirectory, a project generated into an existing repo.

Changed

New projects keep only the path that resolves, ../../../../../node_modules/@storefront-ui/react, which reaches the project root's node_modules from every store built into .out/<store-id>/storefront-unified-nextjs. The compiled CSS is unchanged.

changed The Next.js app pin moves to 16.2.12 and holds back 16.3

The upgrade doesn't change your next pin. Move it to 16.2.12 by hand for the patch fixes, and stay below 16.3 until the livelock is resolved upstream.

What changed ▸
Changed

New projects pin next at 16.2.12 in apps/storefront-unified-nextjs/package.json. The 16.3 line is held back because of a prefetch livelock seen in it. @vue-storefront/next 10.1.0 accepts any Next.js 16.

NuxtAction needed1 change

fixed ProductSlider and the CMS Scrollable forward a normalized class to SfScrollable Action needed

A lockfile that still resolves vue below 3.5.30 builds as before. The moment it resolves 3.5.30 or newer - a refreshed lockfile, a re-resolved @intlify/unplugin-vue-i18n - your copies of the two components stop compiling until you apply the same edit.

step →
What changed ▸
Why

Vue 3.5.30 types the class attribute as ClassValue, which includes false and null. SfScrollable doesn't accept those on wrapperClass, so nuxt build fails with TS2322: Type 'ClassValue' is not assignable to type 'string | Record<string, any> | unknown[] | undefined' in ProductSlider and cms/page/Scrollable once the lockfile resolves that Vue.

Changed

New projects pass the class through Vue's normalizeClass() before forwarding it in both components, and ProductSliderProps.wrapperClass is typed as ClassValue, so it accepts everything a :class binding can hold, conditional values and object syntax included.

SAP Commerce Cloud3 changes

added SAP Commerce Cloud signUserIn forwards extra parameters to the login request

Pass additionalParams only where your SAP instance expects the fields. Move the three packages together; the unified API's major is bookkeeping, not a migration.

What changed ▸
Why

A server-side SAP customization that expects an extra field on login, such as a CAPTCHA response token, had no way to receive it from the storefront.

Changed

signUserIn in @vsf-enterprise/sapcc-api 14.1.0 and @vsf-enterprise/sapcc-types 4.2.0 accepts an optional additionalParams object. With PKCE enabled the parameters are sent as form fields of the authorization server login request; with the ROPC flow they're sent as parameters of the token request. Parameters the OAuth flow itself uses - credentials, CSRF and client fields - can't be overridden.

const tokenResponse = await sdk.commerce.signUserIn({
  username: "test@example.com",
  password: "test123!",
  additionalParams: { recaptchaToken },
});

@vsf-enterprise/unified-api-sapcc 10.0.0 has no API change of its own: it pins @vsf-enterprise/sapcc-api and @vsf-enterprise/sapcc-types as exact peers, and the minor they took moved it a major.

@vsf-enterprise/sapcc-api@vsf-enterprise/sapcc-types@vsf-enterprise/unified-api-sapcc

changed The SAP Commerce packages no longer pull in jsdom

After the upgrade ws no longer resolves through these packages. Nothing to do.

What changed ▸
Changed

@vsf-enterprise/sapcc-api 14.1.0 and @vsf-enterprise/smartedit-api 10.0.0 drop the unused jsdom dependency, which pulled the ws package into projects (npm advisory 1123259, memory exhaustion from tiny fragments).

fixed The SAP ASM installer inserts its panel without overwriting the layout wrapper

If you already installed the module, your wrapper already carries the panel and nothing changes. Only a fresh install behaves differently.

What changed ▸
Why

The Next.js installer replaced app/[locale]/components/root-layout-content-wrapper.tsx with a static template, which silently dropped every earlier edit to that file, including changes made by other modules installed first.

Changed

The installer inserts <AsmPanelClient /> next to {children} with an AST visitor and leaves the rest of the component untouched, so it composes with other modules in any install order. The Nuxt installer still replaces layouts/root.vue, because no other module modifies that layout.

Contentful1 change

changed The Contentful SDK moves to @contentful/live-preview 4

Your app's own @contentful/live-preview pin is yours to move. Keeping ^3 installs two copies of the library side by side; move the pin to ^4.10.20 to share one.

What changed ▸
Changed

@vsf-enterprise/contentful-sdk 10.0.1 depends on @contentful/live-preview ^4.10.20, up from ^3.3.7. New Next.js projects pin the same range in apps/storefront-unified-nextjs/package.json.

@vsf-enterprise/contentful-sdk
SmartEditAction needed3 changes

fixed SmartEdit live preview survives client-side navigation and keeps hidden components visible Action needed

Bumping the SDK brings the idempotent initLivePreview and the Tailwind counter-rules. Setting the cookie, reading the ticket from it and re-running initLivePreview on navigation live in sf-modules/cms-smartedit files your project owns, so the overlay keeps breaking on navigation until you edit them.

step →
What changed ▸
Why

The preview ticket arrived only as the cmsTicketId query parameter, which a client-side navigation drops, so switching pages inside SmartEdit lost preview content and the editing overlay until a full refresh. Calling initLivePreview again installed the injector script twice and stacked SmartEdit's body classes. SmartEdit also injects a .hidden{display:none!important} rule into the storefront iframe, which overrode Tailwind's responsive and focus variants, so components using the re-show idiom - the category navigation bar's hidden md:flex, the breadcrumbs dropdown's hidden group-focus-within:block - disappeared in the preview.

Changed

initLivePreview in @vsf-enterprise/smartedit-sdk 6.1.0 is idempotent and navigation-aware: call it after every page change, the injector is installed once, the body classes are swapped instead of accumulated, and the component-update callback no longer overwrites properties SmartEdit stores on window.smartedit. It takes a previewTokenCookieName option, default vsf-cms-ticket-id, to detect an active preview session, and injects counter-rules for the Tailwind hidden idiom covering the default breakpoints and the --breakpoint-* theme variables the page exposes; tailwindCompatStyles: false opts out. The SDK exports SMARTEDIT_PREVIEW_TOKEN_COOKIE_NAME and isValidCmsTicketId. The module files persist the ticket in that session cookie and read it back on every page.

@vsf-enterprise/smartedit-sdk

fixed SmartEdit preview renders multi-column page templates like the live storefront Action needed

The module files are copied into your project when the module is installed, so the fix is a hand edit in your copies.

step →
What changed ▸
Why

Content slots rendered in preview mode lost their layout-transparent contents wrappers, because the SmartEdit classes replaced the class instead of merging with it, so multi-column template sections such as Section2A and Section2B collapsed into stacked columns. In the Nuxt storefront the wrappers had lost the class entirely, so regular pages were affected too.

Changed

The module's render-cms-content.tsx merges contents with the SmartEdit classes, and RenderSlot.vue sets a static class="contents" on both wrappers, which Vue merges with the bound classes.

changed @vsf-enterprise/smartedit-api moves to 10.0.0 in lockstep with the SAP Commerce API

Move it together with @vsf-enterprise/sapcc-api 14.1.0. Nothing in your code changes.

What changed ▸
Changed

@vsf-enterprise/smartedit-api 10.0.0 has no API change. It pins @vsf-enterprise/sapcc-api as an exact peer, and that package's minor moved it a major. It also drops the unused jsdom dependency.

@vsf-enterprise/smartedit-api
CI / deployment workflows1 change

added Generated CI and CD workflows cap every job at 30 minutes

Your existing workflow files aren't touched by the upgrade. Add timeout-minutes: 30 under each of those jobs yourself if you want the same cap.

What changed ▸
Why

A hanging step in continuous-integration.yml or continuous-delivery.yml kept a runner busy until GitHub's six-hour default gave up.

Changed

Projects generated at this release have timeout-minutes: 30 on the run-ci job in .github/workflows/continuous-integration.yml and on the chooseStoresToDeploy and deploy jobs in .github/workflows/continuous-delivery.yml.

On this page