@salesforce/retail-react-app 10.4.0-nightly-20261110080254 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,29 +1,34 @@
1
- ## v10.4.0-dev (Oct 20, 2026)
2
- - [Bugfix] Keep PDPs working when embedded agent configuration is absent. Preserve failed shopper-agent handoff questions with retry feedback or MIAW manual-send guidance, avoid send-method fallthrough after delivery errors, and remove unused handoff scaffolding. [#4101](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4101)
3
- - [Enhancement] Add boolean `preWarm` configuration for the inline agent widget (enabled by default). The widget starts its SCRT2 handshake when at least half visible; set `preWarm: false` for a cold connection on the first question. Shopper-agent mode uses the handoff shim without starting an embedded session. Includes inline-agent-widget PRs #26, #27, and #28. [#4101](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4101)
1
+ ## v11.0.0 (Nov 10, 2026)
4
2
  - [Feature] Export `getDynamicRoutes(config)` from `app/routes` so projects that extend this template can include the config-gated routes (reset password and passwordless login landing pages, social login redirect, guest order lookup, Shopper Agent express payment).
5
3
  - [Security] Require `login_id` and `channel_id` on passwordless OTP token exchange (`loginPasswordless`) from the login page, auth modal, and one-click checkout contact/registration flows, so SLAS can reject mismatched identifiers. Map SLAS 400 missing/mismatched-identifier errors and passwordless rate limits (429) to existing OTP/login UX, and map password/action rate limits (429) to the too-many-password-reset-attempts UX. Depends on `@salesforce/commerce-sdk-react@6.0.0` (breaking major; pairs with `commerce-sdk-isomorphic@5.8.0`). [#4075](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4075)
6
4
  - [Security] Passwordless magic links no longer carry the shopper email. When the shopper opens the link in the browser that requested it, the login landing page sends the email remembered at request time (stored in `localStorage` for 15 minutes) as `login_id`; in any other browser or device it keeps the token and asks the shopper to confirm their email to finish signing in. A `login_id` query parameter (for example in a merchant-configured `email` mode link) is still accepted. Token exchange failures on the landing page are now shown instead of leaving the shopper on the sign-in page with no feedback. **Upgrade note:** if your project overrides the login page, auth modal, or one-click checkout contact/registration components, call `rememberPasswordlessLoginId(email)` after each successful `authorizePasswordless` call so same-browser magic links sign in without the extra step. Both remembered emails are cleared on logout; if you add your own logout action, call `clearRememberedAuthEmails()` there too. [#4092](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4092)
7
5
  - [Feature] The reset password landing page prefills the email when the shopper opens the link in the browser that requested it (stored in `localStorage` for 15 minutes, separately from the passwordless email, and cleared after a successful reset). In any other browser the shopper enters it as before. The reset link still carries only the token. [#4092](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4092)
8
6
  - [Enhancement] Update guest order lookup copy per the UI text review: the request page now reads "Order Lookup" with revised intro text, title-case field labels, and a "Find Order" button; the verify page uses "Enter the verification code that we sent to {email}.". Remove the placeholder "Order Status" footer link, which pointed to the home page. [#4090](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4090)
9
7
  - [Bugfix] Keep Commerce Client shopper-agent product links on the PWA Kit storefront. The agent backend sends product links as SFRA URLs on the backend origin (e.g. `/en_US/<slug>-<id>.html`), which the widget's `onNavigate` never sees, so clicking a product card left for the SFRA PDP. The storefront now reads the product id from those links in the widget's product slots (cards, product detail title, comparison columns), rewrites their `href` to the PWA product page, and routes plain clicks through the router. Links in agent message text are left alone. When the product id is not certain from the URL (the last path segment contains `-`, so it may be a name slug and id, or an id that contains `-`), it is resolved through Shopper SEO `getUrlMapping`; if that finds no product the link keeps its backend URL rather than pointing at a storefront 404. [#4076](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4076)
8
+ - [Enhancement] Increase the `main.js` bundle-size budget from 109 kB to 111 kB for the Shopper Agent express-payment iframe framework, and the `vendor.js` budget from 403 kB to 412 kB for the embedded Page Designer content blocks. [#4063](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4063), [#4042](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4042)
10
9
  - [Feature] Add a neutral Shopper Agent express-payment iframe framework with cart and product routes, a merchant provider lifecycle contract, validated iframe-to-host events, and automatic iframe sizing. [#4063](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4063)
11
- - [Changed] Rename `pwakit-notify` notification types to flow-based names: `passwordless-magic-link` → `passwordless-login`, `otp` → `password-registration`. Adds new `passwordless-registration` type for new-user magic-link delivery — intentionally skips the anti-enumeration account-existence check (correct for the new-user registration flow). The cartridge changes are aligned 1-to-1 with sf-next PR #2946. [Security] Cross-validate `email_id` from the SLAS callback JWT against the request body in the passwordless-login callback, preventing email relay via an arbitrary-recipient registration email when `register=true`.
10
+ - [Changed] Rename `pwakit-notify` notification types to flow-based names: `passwordless-magic-link` → `passwordless-login`, `otp` → `password-registration`. Adds new `passwordless-registration` type for new-user magic-link delivery — intentionally skips the anti-enumeration account-existence check (correct for the new-user registration flow).
11
+ - [Security] Cross-validate `email_id` from the SLAS callback JWT against the request body in the passwordless-login callback, preventing email relay via an arbitrary-recipient registration email when `register=true`.
12
12
  - [Security] Cross-validate `email_id` and `token` claims in the SLAS callback JWT against the request body in the registration-verification callback (`/registration-verification-callback`), preventing email-relay attacks where a caller with a valid tenant-signed guest token could trigger OTP delivery to an arbitrary address. [#4067](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4067)
13
13
  - [Bugfix] Gate the GLO magic-link auto-submit on `emailFromContext` (email present in router state) rather than the derived `email` variable, so arriving directly via magic link no longer attempts an auto-submit before the user enters their email address. [#4067](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4067)
14
14
  - [Enhancement] Update inline agent widget UMD bundle: add follow-up suggestion pills from agent responses, and send `commerce_agent_context` as a TextValue JSON string. Bump the embedded agent `capabilitiesVersion` to `66` (new `DEFAULT_EMBEDDED_AGENT_CAPABILITIES_VERSION` constant, separate from the Commerce Client's `65`) for context-variable support. Built from [inline-agent-widget#17](https://github.com/SalesforceCommerceCloud/inline-agent-widget/pull/17) and [inline-agent-widget#18](https://github.com/SalesforceCommerceCloud/inline-agent-widget/pull/18). [#4061](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4061)
15
- - [Bugfix] Temporarily pin transitive Vitest to 4.1.11 to avoid an npm 10 dependency resolver crash during fresh installs while the Vitest browser packages require 4.1.11.
16
- - [Bugfix] Fix `useScript` remount when the script tag is already in the DOM. ShopperAgent (and other consumers) stayed at `loaded: false` after a remount because the existing tag never fired `load` again, so the Commerce Client widget did not re-inject. The hook now stamps `data-script-status` on load/error and only reports `loaded: true` when that status is `ready`; an in-flight tag stays unloaded until it finishes. [#4062](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4062) (W-24380725)
17
- - [Bugfix] Route Commerce Client widget product/checkout/markdown clicks through the storefront `useNavigation` router via the widget's `onNavigate` callback, so same-tab same-origin links stay in the SPA instead of triggering a full page load. Requires Commerce Client widget `1.48.0+`. `cc_openLinksInNewTab: "true"` is unchanged and still opens a new tab. Also fixes `buildUrl` (`createUrlTemplate`) when site/locale are configured as `query_param`: paths that already have a query string or hash now get site/locale merged into the existing query instead of a second `?` appended (which previously corrupted params like `q`/`pid` and swallowed the hash).
15
+ - [Bugfix] Temporarily pin transitive Vitest to 4.1.11 to avoid an npm 10 dependency resolver crash during fresh installs while the Vitest browser packages require 4.1.11. [#4064](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4064)
16
+ - [Bugfix] Fix `useScript` remount when the script tag is already in the DOM. ShopperAgent (and other consumers) stayed at `loaded: false` after a remount because the existing tag never fired `load` again, so the Commerce Client widget did not re-inject. The hook now stamps `data-script-status` on load/error and only reports `loaded: true` when that status is `ready`; an in-flight tag stays unloaded until it finishes. [#4062](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4062)
17
+ - [Bugfix] Route Commerce Client widget product/checkout/markdown clicks through the storefront `useNavigation` router via the widget's `onNavigate` callback, so same-tab same-origin links stay in the SPA instead of triggering a full page load. Requires Commerce Client widget `1.48.0+`. `cc_openLinksInNewTab: "true"` is unchanged and still opens a new tab. Also fixes `buildUrl` (`createUrlTemplate`) when site/locale are configured as `query_param`: paths that already have a query string or hash now get site/locale merged into the existing query instead of a second `?` appended (which previously corrupted params like `q`/`pid` and swallowed the hash). [#4035](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4035)
18
18
  - [Enhancement] Update inline agent widget UMD bundle: replace the text-prefix product-context hack with the proper SCRT2 v2 session-context API. Product context is now sent as a `commerce_agent_context` StructuredValue in the SessionContextSet envelope, leaving the shopper's message text unchanged. Default `capabilitiesVersion` bumped to `"65"`. Built from [inline-agent-widget#3](https://github.com/SalesforceCommerceCloud/inline-agent-widget/pull/3). [#4054](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4054)
19
19
  - [Feature] Add `app_pwakit_base` B2C cartridge for transactional email delivery via ISML templates and `dw/net/Mail`. GLO access-code emails use the `sfcc.app.order.sendOrderAccessCode` hook; passwordless login, OTP verification, and password reset use the `pwakit-notify` Custom REST API (`POST .../organizations/{orgId}/notify?siteId={siteId}`, guarded by the `c_pwakit_notify` SLAS scope). Deploy with `npm run deploy:cartridge` from `packages/template-retail-react-app` (uses `b2c-cli` with `--reload` to upload the cartridge and re-register the SCAPI endpoint in one step), then import `OrganizationPreferences.xml` to enable the `pwakitNotifyEnabled` and `pwakitStorefrontHost` site preferences. **Required: create a dedicated private SLAS client** (`notifyClientId`) scoped only to `c_pwakit_notify`, set `PWA_KIT_NOTIFY_SLAS_CLIENT_SECRET` to its secret, and add `notifyClientId` to `commerceAPI.parameters` in your config. **Do not add `c_pwakit_notify` to the shopper/public client** — doing so makes browser-minted guest tokens capable of calling the notification API directly, reopening the email-relay risk this design is intended to close. See `cartridges/app_pwakit_base/README.md` for full setup steps. [#4012](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4012)
20
20
  - [Breaking change] `config/default.js` now sets `mode: 'callback'` for both `passwordless` and `resetPassword` flows (previously `'email'`). This routes password-reset confirmation through the SSR `/reset-password-callback` route and the new `pwakit-notify` cartridge. **Projects that adopt this config without deploying the cartridge will have broken password resets** — there is no `resetPassword.enabled` guard (unlike `passwordless.enabled`). Either deploy the cartridge before adopting the config change, or explicitly override `resetPassword.mode` back to `'email'`. [#4012](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4012)
21
+ - [Breaking change] Marketing Cloud email delivery is removed from `app/ssr.js`. The `emailLink` function and the `MARKETING_CLOUD_CLIENT_ID`, `MARKETING_CLOUD_CLIENT_SECRET`, `MARKETING_CLOUD_SUBDOMAIN`, `MARKETING_CLOUD_PASSWORDLESS_LOGIN_TEMPLATE` and `MARKETING_CLOUD_RESET_PASSWORD_TEMPLATE` environment variables are no longer read. Passwordless login, password reset and registration emails are now sent through the `pwakit-notify` endpoint of the `app_pwakit_base` cartridge. Projects that send these emails through Marketing Cloud must deploy the cartridge and configure `notifyClientId` and `PWA_KIT_NOTIFY_SLAS_CLIENT_SECRET`, or keep their own copy of the previous `emailLink` implementation in an overridden `ssr.js`. [#4012](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4012)
22
+ - [Breaking change] `config/default.js` now sets `passwordless.enabled` to `true` (previously `false`); projects generated with `create-app` use a bootstrap config that keeps it `false`. Combined with `mode: 'callback'`, projects that inherit this config need the `app_pwakit_base` cartridge deployed or must set `passwordless.enabled` back to `false`. [#4012](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4012)
23
+ - [Breaking change] `config/default.js` now sets `guestOrderLookup.enabled` to `true` (previously `false`); projects generated with `create-app` use a bootstrap config that does not enable it. This enables the `/order-lookup` routes and the order access code email, which is sent by the cartridge's `sendOrderAccessCode` hook. Deploy the `app_pwakit_base` cartridge or set `guestOrderLookup.enabled` back to `false`. [#4012](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4012)
24
+ - [Breaking change] Projects that override passwordless or reset password code must update their calls: `loginPasswordless` now needs `login_id` (the email sent to `authorizePasswordless`) and `channel_id` (the site id), and `resetPassword` now needs `user_id` (the email the reset was requested for). See the `@salesforce/commerce-sdk-react` 6.0.0 changelog. [#4075](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4075), [#4092](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4092)
21
25
  - [Bugfix] Fix GLO `/api/guestOrderLookup/verify` and related server endpoints returning 401 when `enablePWAKitPrivateClient` is enabled. The `cc-nx-g_` cookie stores the SLAS refresh token, not the access token — the access token is never stored in any cookie. All GLO server endpoints now read the SLAS JWT from the `Authorization` header sent by the client (via `useAccessToken().getTokenWhenReady()`), which works correctly in both public and private client modes. [#4012](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4012)
22
- - [Enhancement] End the Commerce Client shopper-agent conversation when a registered shopper logs out, instead of re-linking it to the guest. Uses the widget's `eventHandlers.session.endConversation()` (`cc_cdnVersion` >= `1.39.0`), falls back to `resetConversation()` on `1.36.0`–`1.38.x`, and to re-linking before `1.36.0`. Also keeps the widget mounted through the login/logout basket reload so the bundle is not injected twice. [#4051](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4051) (W-24207318)
26
+ - [Enhancement] End the Commerce Client shopper-agent conversation when a registered shopper logs out, instead of re-linking it to the guest. Uses the widget's `eventHandlers.session.endConversation()` (`cc_cdnVersion` >= `1.39.0`), falls back to `resetConversation()` on `1.36.0`–`1.38.x`, and to re-linking before `1.36.0`. Also keeps the widget mounted through the login/logout basket reload so the bundle is not injected twice. [#4051](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4051)
27
+ - [Enhancement] Commerce Client shopper identity linking now retrieves the auth link key via the widget's `window.CimulateMessaging.getAuthLinkKey()` when the bundle exposes it, instead of calling SCRT2's `/iamessage/api/v2/authorization/authlink` endpoint directly with the Commerce Client JWT. Older bundles without the function fall back to the direct SCRT2 call (feature-detected, not version-checked). A `getAuthLinkKey()` rejection is surfaced as a linking failure rather than retried via the fallback, since both paths hit the same endpoint. [#4050](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4050)
28
+ - [Feature] Add embedded Page Designer content-block support to the retail template, including an Announcement Banner content component, a preview-only Header layout, and a chrome-free `/preview/component` route for standalone component previews. [#4042](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4042)
29
+ - [Feature] Add opener question pills to the inline agent widget on the PDP. The Product Detail page parses SCAPI `c_pdpQuestions` (a JSON-encoded array of strings published by Commerce Content Studio) and forwards it to the widget via the new `pdp-questions` attribute. The widget renders up to 5 tappable pills above the input; tapping a pill sends it through the same message path as typed input, and pills auto-hide once the shopper engages. On SPA navigation between PDPs the pills re-appear for the new product's list. Malformed or absent `c_pdpQuestions` renders no pills.
23
30
 
24
31
  ## v10.3.0 (Oct 08, 2026)
25
- - [Enhancement] Commerce Client shopper identity linking now retrieves the auth link key via the widget's `window.CimulateMessaging.getAuthLinkKey()` when the bundle exposes it, instead of calling SCRT2's `/iamessage/api/v2/authorization/authlink` endpoint directly with the Commerce Client JWT. Older bundles without the function fall back to the direct SCRT2 call (feature-detected, not version-checked). A `getAuthLinkKey()` rejection is surfaced as a linking failure rather than retried via the fallback, since both paths hit the same endpoint. [#4050](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4050) (W-24076703)
26
- - [Feature] Add embedded Page Designer content-block support to the retail template, including an Announcement Banner content component, a preview-only Header layout, and a chrome-free `/preview/component` route for standalone component previews. [#4042](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4042)
27
32
  - [Enhancement] Rename `INLINE_AGENT_WIDGET_COMMERCE_SETTINGS` environment variable to `EMBEDDED_AGENT_COMMERCE_SETTINGS` and config key `inlineAgentWidget` to `embeddedAgent` to align with updated product terminology. [#4041](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4041)
28
33
  - [Feature] Add a configurable `cc_enableImageUpload` toggle for the Commerce Client (Embedded Messaging) shopper-agent widget, defaulting to `'false'` and forwarded to the bundle as `messagingConfig.enableImageUpload`. Set it to `'true'` via `COMMERCE_AGENT_SETTINGS` to let shoppers upload images in the chat; requires a Commerce Client bundle that supports image upload (`cc_cdnVersion` >= `1.44.0`) plus the backend file-upload prerequisites. Also adds `blob:` and `data:` to the `img-src` CSP directive in `app/ssr.js` so the widget can render the pre-send preview (a `blob:` URL) and the sent-image thumbnail (a `data:` URL); the widget renders images locally only, so no image host is added. [#4030](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4030)
29
34
  - [Feature] Add the `dw_attribution` first-touch marketing-attribution cookie (W-23493129). The app writes a sanitized, allowlist-only campaign cookie client-side on load so Salesforce CIP can attribute placed orders back to the campaign that drove a headless-storefront visit. Writing in the browser (not on SSR) means it also fires on a CDN document-cache hit — where the SSR handler never runs — and lets the app clear the cookie the instant a shopper opts out; campaign landing pages stay fully cacheable. The Secure + `SameSite=Lax` cookie has a 30-minute write-once TTL and stores percent-encoded `key=value` pairs (`utm_source`/`utm_medium`/`utm_campaign`/`utm_term`/`utm_content`, `gclid`, `fbclid`, `msclkid`, and a sanitized external `ref` — origin, path, and its own allowlisted params only); no free-text query params are captured. It is write-once (first touch wins) and honors Do Not Track (`dw_dnt=1`): while opted out it captures nothing and expires any existing cookie.
@@ -38,8 +43,7 @@
38
43
  - [Enhancement] Update inline agent widget UMD bundle: auto-restart session on expiry instead of showing an error. The widget silently resets to idle and the next message starts a fresh session transparently. [#4015](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4015)
39
44
  - [Enhancement] Update inline agent widget UMD bundle: show user question bubble, add 4,000-character message limit, improve error handling with generic user-facing messages, fix 403 stale-conversation reset, and hide restored session Q&A on page reload. [#4032](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4032)
40
45
  - [Enhancement] Update inline agent widget UMD bundle: suppress agent welcome message on conversation start, and clear stale question/answer when navigating between product pages via SPA routing.
41
- - [Feature] Add opener question pills to the inline agent widget on the PDP (@W-24333476). The Product Detail page parses SCAPI `c_pdpQuestions` (a JSON-encoded array of strings published by Commerce Content Studio) and forwards it to the widget via the new `pdp-questions` attribute. The widget renders up to 5 tappable pills above the input; tapping a pill sends it through the same message path as typed input, and pills auto-hide once the shopper engages. On SPA navigation between PDPs the pills re-appear for the new product's list. Malformed or absent `c_pdpQuestions` renders no pills.
42
- - [Feature] Add Guest Order Lookup (GLO): unauthenticated shoppers can look up an order by order number and email, verify ownership via a time-limited 6-digit access code (valid for 15 minutes, reusable within the window), and view a filtered order detail page (shipment tracking, items, payment summary, shipping address). Server-side endpoints (`/api/guestOrderLookup/{verify,order,oms-meta,cancel,return}`) receive the SLAS access token via the `Authorization` header (sent by the client using `useAccessToken().getTokenWhenReady()`), call SCAPI with the shopper's bearer token, and filter the response to safe fields. The verify endpoint writes a `cc-goa_` HttpOnly cookie binding the order number to the verified access code; all subsequent order endpoints validate this cookie before proxying. [#3982](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3982)
46
+ - [Feature] Add Guest Order Lookup (GLO): unauthenticated shoppers can look up an order by order number and email, verify ownership via a time-limited 6-digit access code (valid for 15 minutes, reusable within the window), and view a filtered order detail page (shipment tracking, items, payment summary, shipping address). Server-side endpoints (`/api/guestOrderLookup/{verify,order,oms-meta,cancel,return}`) read the SLAS access token from the HttpOnly `cc-at_*` cookie, call SCAPI with the shopper's bearer token, and filter the response to safe fields. The verify endpoint writes a `cc-goa_` HttpOnly cookie binding the order number to the verified access code; all subsequent order endpoints validate this cookie before proxying. [#3982](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3982)
43
47
  - [Feature] Forward two backend-gating signals on the Commerce Client shopper-agent widget's `routingAttributes`: `clientVersion` (sourced from `cc_cdnVersion`, so the runtime can gate rich components on the client's capability version; omitted when unset) and `isCartMgmtSupported` (string `'true'`/`'false'`, default `'false'`, read from `cc_routingAttributes`). Brings the PWA commerce-client path to parity with the SFRA cartridge.
44
48
  - [Feature] Enable shopper identity (customer context) for the Commerce Client (Cimulate) shopper-agent provider. Unlike MIAW, Commerce Client widgets do not expose `getAuthLinkKey`, so the browser extracts the Commerce Client JWT from the deployment-scoped `cim_af_ct_<orgId>_<embeddedServiceName>` key (`sessionStorage` is authoritative; `localStorage` is a compatibility fallback) and calls SCRT2's `/iamessage/api/v2/authorization/authlink` endpoint directly (`auth-link-client.js`) with an `Authorization: Bearer <jwt>` header to obtain an `auth_link_key`. The SCRT2 origin comes from `scrt2Url` in `COMMERCE_AGENT_SETTINGS` (a different host from Core's My Domain) and is strictly validated before the token is sent (HTTPS only; no credentials, port, path, query, or fragment) against the Salesforce SCRT2 domain allowlist, with the request hardened (`credentials: 'omit'`, `redirect: 'error'`, `referrerPolicy: 'no-referrer'`, `cache: 'no-store'`) and bounded by a 10s abort timeout. The `auth_link_key` is then forwarded to the existing Token Bridge to bind the conversation to the shopper's SLAS session. Linking is idempotent — deduped by `conversationId` + SLAS identity — and re-runs both on a new conversation (`onCimulateWidgetReady`) and on a SLAS identity transition (guest ↔ registered / account switch), gated on `my_domain` resolution. [#3943](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3943) [#3981](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/3981)
45
49
  - [Bugfix] Restore Commerce Client shopper identity linking with widget 1.34.0+: read persisted conversation IDs from `localStorage` with a legacy `sessionStorage` fallback, and continue checking the other store when one contains the previously linked JWT so a stale session token cannot hide a newer local token. [#4033](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/4033) (W-24293344)
@@ -5,73 +5,15 @@
5
5
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
6
  */
7
7
 
8
- import React, {useEffect, useState} from 'react'
8
+ import React from 'react'
9
9
  import {Helmet} from 'react-helmet'
10
10
  import PropTypes from 'prop-types'
11
- import {FormattedMessage} from 'react-intl'
12
- import {Alert, Box, Button, Text} from '@salesforce/retail-react-app/app/components/shared/ui'
13
11
  import useInlineAgentWidget, {
14
12
  isConfigured
15
13
  } from '@salesforce/retail-react-app/app/hooks/use-inline-agent-widget'
16
- import {handoffToShopperAgent} from '@salesforce/retail-react-app/app/utils/shopper-agent-handoff'
17
14
 
18
- const VALID_MODES = ['inline', 'shopper-agent', 'hybrid']
19
- const HANDOFF_EVENT = 'inline-agent-widget:handoff'
20
-
21
- const InlineAgentWidget = ({config, pdpQuestions, productName, productId}) => {
15
+ const InlineAgentWidget = ({config, pdpQuestions, productName}) => {
22
16
  const containerRef = useInlineAgentWidget(config, {pdpQuestions, productName})
23
- const [failedQuestions, setFailedQuestions] = useState([])
24
- const mode = config?.mode
25
- const configured = isConfigured(config)
26
-
27
- useEffect(() => {
28
- if (mode !== undefined && !VALID_MODES.includes(mode)) {
29
- console.warn(
30
- `InlineAgentWidget: unknown mode "${mode}". Expected one of ${VALID_MODES.join(
31
- ', '
32
- )}. Falling back to 'inline'.`
33
- )
34
- }
35
- }, [mode])
36
-
37
- useEffect(() => {
38
- setFailedQuestions([])
39
- if (!configured || mode !== 'shopper-agent') return
40
- if (typeof window === 'undefined') return
41
-
42
- const controller = new AbortController()
43
- const onHandoff = async (ev) => {
44
- const question = ev.detail?.question
45
- if (typeof question !== 'string' || !question.trim()) return
46
- setFailedQuestions((previous) => previous.filter((item) => item.question !== question))
47
- let result
48
- try {
49
- result = await handoffToShopperAgent({
50
- question,
51
- signal: controller.signal,
52
- productContext: {
53
- productId,
54
- productName,
55
- pdpUrl: window.location.href
56
- }
57
- })
58
- } catch (error) {
59
- console.error('Shopper Agent handoff failed', error)
60
- }
61
- if (!controller.signal.aborted && !result?.seeded) {
62
- setFailedQuestions((previous) => [
63
- ...previous.filter((item) => item.question !== question),
64
- {question, provider: result?.provider}
65
- ])
66
- }
67
- }
68
-
69
- document.addEventListener(HANDOFF_EVENT, onHandoff)
70
- return () => {
71
- controller.abort()
72
- document.removeEventListener(HANDOFF_EVENT, onHandoff)
73
- }
74
- }, [configured, mode, productId, productName])
75
17
 
76
18
  if (!isConfigured(config)) return null
77
19
 
@@ -81,40 +23,6 @@ const InlineAgentWidget = ({config, pdpQuestions, productName, productId}) => {
81
23
  <script src="/static/inline-agent-widget.umd.js" async></script>
82
24
  </Helmet>
83
25
  <div ref={containerRef} />
84
- {failedQuestions.map(({question, provider}) => (
85
- <Alert key={question} status="error" role="alert" marginTop={2}>
86
- <Box>
87
- <Text>
88
- {provider === 'miaw' ? (
89
- <FormattedMessage
90
- id="inline_agent_widget.message.manual_handoff"
91
- defaultMessage="This chat cannot receive questions automatically. Copy your question below and send it in the chat."
92
- />
93
- ) : (
94
- <FormattedMessage
95
- id="inline_agent_widget.message.handoff_failed"
96
- defaultMessage="We couldn't send your question to the assistant. Please try again."
97
- />
98
- )}
99
- </Text>
100
- <Text>{question}</Text>
101
- {provider !== 'miaw' && (
102
- <Button
103
- onClick={() =>
104
- document.dispatchEvent(
105
- new CustomEvent(HANDOFF_EVENT, {detail: {question}})
106
- )
107
- }
108
- >
109
- <FormattedMessage
110
- id="inline_agent_widget.button.retry"
111
- defaultMessage="Try again"
112
- />
113
- </Button>
114
- )}
115
- </Box>
116
- </Alert>
117
- ))}
118
26
  </>
119
27
  )
120
28
  }
@@ -125,12 +33,22 @@ InlineAgentWidget.propTypes = {
125
33
  scrt2Url: PropTypes.string,
126
34
  orgId: PropTypes.string,
127
35
  esDeveloperName: PropTypes.string,
128
- placeholder: PropTypes.string,
129
- mode: PropTypes.oneOf(['inline', 'shopper-agent', 'hybrid'])
36
+ placeholder: PropTypes.string
130
37
  }),
38
+ /**
39
+ * Opener questions for the current product — rendered as tappable pills
40
+ * above the widget input. Typically sourced from the SCAPI
41
+ * `c_pdpQuestions` custom attribute (parsed via `parsePdpQuestions`).
42
+ */
131
43
  pdpQuestions: PropTypes.arrayOf(PropTypes.string),
132
- productName: PropTypes.string,
133
- productId: PropTypes.string
44
+ /**
45
+ * Current product's display name. Templated into the InputBar
46
+ * placeholder — "Ask me anything about <productName>" — so the shopper
47
+ * sees product-grounded prompting on PDP load. Sourced from the SCAPI
48
+ * `name` standard field. When absent, the widget falls back to the
49
+ * generic "Ask me anything" string.
50
+ */
51
+ productName: PropTypes.string
134
52
  }
135
53
 
136
54
  export default InlineAgentWidget
@@ -707,8 +707,7 @@ const CommerceClientAgentWindow = ({
707
707
  cc_openLinksInNewTab,
708
708
  cc_showProductDescription,
709
709
  cc_showProductCaptions,
710
- cc_promptsConfig,
711
- cc_enablePageContext
710
+ cc_promptsConfig
712
711
  } = commerceAgentConfiguration
713
712
 
714
713
  const navigate = useNavigation()
@@ -1183,8 +1182,7 @@ const CommerceClientAgentWindow = ({
1183
1182
  onNavigate,
1184
1183
  showProductDescription: toOptionalWidgetBoolean(cc_showProductDescription),
1185
1184
  showProductCaptions: toOptionalWidgetBoolean(cc_showProductCaptions),
1186
- promptsConfig: cc_promptsConfig,
1187
- enablePageContext: toOptionalWidgetBoolean(cc_enablePageContext)
1185
+ promptsConfig: cc_promptsConfig
1188
1186
  }),
1189
1187
  [
1190
1188
  commerceClientElementId,
@@ -1221,8 +1219,7 @@ const CommerceClientAgentWindow = ({
1221
1219
  onNavigate,
1222
1220
  cc_showProductDescription,
1223
1221
  cc_showProductCaptions,
1224
- cc_promptsConfig,
1225
- cc_enablePageContext
1222
+ cc_promptsConfig
1226
1223
  ]
1227
1224
  )
1228
1225
 
@@ -87,8 +87,7 @@ const injectCommerceClientWidget = ({
87
87
  onNavigate,
88
88
  showProductDescription,
89
89
  showProductCaptions,
90
- promptsConfig,
91
- enablePageContext
90
+ promptsConfig
92
91
  } = {}) => {
93
92
  if (!onClient) return false
94
93
 
@@ -116,9 +115,6 @@ const injectCommerceClientWidget = ({
116
115
  if (typeof showProductCaptions === 'boolean') {
117
116
  messagingConfig.showProductCaptions = showProductCaptions
118
117
  }
119
- if (typeof enablePageContext === 'boolean') {
120
- messagingConfig.enablePageContext = enablePageContext
121
- }
122
118
 
123
119
  commerceClient.injectMessagingWidget({
124
120
  elementId,
@@ -39,12 +39,8 @@ const ensureWidget = () => {
39
39
  return readyPromise
40
40
  }
41
41
 
42
- const isConfigured = (config) => {
43
- if (!config?.enabled) return false
44
- // shopper-agent never opens a SCRT2 session — only `enabled` matters.
45
- if (config?.mode === 'shopper-agent') return true
46
- return Boolean(config?.scrt2Url && config?.orgId && config?.esDeveloperName)
47
- }
42
+ const isConfigured = (config) =>
43
+ Boolean(config?.enabled && config?.scrt2Url && config?.orgId && config?.esDeveloperName)
48
44
 
49
45
  // Stable string key for the pdpQuestions array so the sync effect's dependency
50
46
  // is by value, not identity. An empty/undefined list serializes to "" so it
@@ -57,7 +53,6 @@ const useInlineAgentWidget = (config, {pdpQuestions, productName} = {}) => {
57
53
  const containerRef = useRef(null)
58
54
  const configured = isConfigured(config)
59
55
  const questionsKey = pdpQuestionsKey(pdpQuestions)
60
- const mode = config?.mode
61
56
 
62
57
  useEffect(() => {
63
58
  if (typeof window === 'undefined') return
@@ -77,18 +72,13 @@ const useInlineAgentWidget = (config, {pdpQuestions, productName} = {}) => {
77
72
  if (containerRef.current.querySelector('inline-agent-widget')) return
78
73
 
79
74
  const el = document.createElement('inline-agent-widget')
80
- if (mode) el.setAttribute('mode', mode)
81
- // SCRT2 fields are only set when they're real — shopper-agent mode
82
- // doesn't need them and the UMD's config-validation skips the check
83
- // when `mode="shopper-agent"`.
84
- if (config.scrt2Url) el.setAttribute('scrt2-url', config.scrt2Url)
85
- if (config.orgId) el.setAttribute('org-id', config.orgId)
86
- if (config.esDeveloperName) el.setAttribute('es-developer-name', config.esDeveloperName)
75
+ el.setAttribute('scrt2-url', config.scrt2Url)
76
+ el.setAttribute('org-id', config.orgId)
77
+ el.setAttribute('es-developer-name', config.esDeveloperName)
87
78
  el.setAttribute('capabilities-version', DEFAULT_EMBEDDED_AGENT_CAPABILITIES_VERSION)
88
79
  if (config.placeholder) el.setAttribute('placeholder', config.placeholder)
89
80
  if (config.persistSession !== false) el.setAttribute('persist-session', '')
90
81
  if (config.enableLogging) el.setAttribute('enable-logging', '')
91
- if (config.preWarm !== false) el.setAttribute('pre-warm', '')
92
82
  // Seed pdp-questions at create time when we already have them, so the
93
83
  // pills paint in the same frame the widget mounts (no visible pop-in
94
84
  // from the attribute-sync effect below).
@@ -104,7 +94,7 @@ const useInlineAgentWidget = (config, {pdpQuestions, productName} = {}) => {
104
94
  return () => {
105
95
  el.remove()
106
96
  }
107
- }, [configured, ready, config, mode])
97
+ }, [configured, ready, config])
108
98
 
109
99
  // Keep the pdp-questions attribute in sync with the current product. Runs
110
100
  // separately from element creation so a variant switch / SPA nav to a new
@@ -815,7 +815,6 @@ const ProductDetail = () => {
815
815
  productResponse?.c_pdpQuestions
816
816
  )}
817
817
  productName={productResponse?.name}
818
- productId={productResponse?.id}
819
818
  />
820
819
  }
821
820
  />