# @sonordev/site-kit Changelog

## 7.2.0

Forms, popups and booking now work the way AI agent browsers, autofill and screen readers expect, Sonor's schema can't put a template's placeholders on a live page, and the retired Engage module is gone from the kit. No site changes needed: update the package and rebuild.

### Engage is gone

Engage was retired in Sonor; its chat lives in Messages and its popups in Website. The kit now matches.

- **Chat and popups load on their own.** Website chat is `@sonordev/site-kit/chat` and popups are `@sonordev/site-kit/website/popups`. `SiteKitLayout` mounts `SiteChat` and `SitePopups` as separate deferred siblings, so a site with popups off never downloads the chat, and the other way round. The popups entry is 16% smaller.
- **Popups render from blocks only.** A popup made in Engage Studio (one without blocks) isn't drawn: make it again in Website → Popups & Banners. `DesignRenderer` and its types are removed.
- **New:** `SiteChat` in `./chat`, the standalone chat mount (idle-deferred, gated like analytics), and the popup types under their own names in `./website/popups` (`SitePopup`, `SitePopupConfig`, `SitePopupTargeting`, `SitePopupTrigger`, `SitePopupType`).
- **`@sonordev/site-kit/engage` still builds through 7.x.** `ChatWidget` and the chat types re-export from it, the popup types keep their old names (`EngageElement` is `SitePopup`), and `EngageWidget` draws `SiteChat` and `SitePopups`. sonor-setup's codemod moves the imports. It's removed in 8.0.

See [Website chat](https://sonor.dev/site-kit/chat) and [Popups and banners](https://sonor.dev/site-kit/popups).

### Agent-ready forms

- **Autocomplete tokens.** Name, email, phone, company, address and website fields carry the matching `autocomplete` token (`given-name`, `email`, `tel`, `organization`, `postal-code`, and so on), read from the field's CRM destination, then its type, slug or a short label. A field it can't match confidently gets none, since a wrong token is worse than no token.
- **Accessible wiring.** Every control is tied to its label, help text and error (`aria-describedby`, `aria-invalid`, errors announced with `role="alert"`). Radio and checkbox groups are named by their question, rating stars say which one is chosen, and the required asterisk is hidden from assistive tech, since `required` already says it.
- **WebMCP.** The interactive form declares itself as a tool (`toolname`, `tooldescription`, and `toolparamdescription` on fields with help text), so a browser that supports WebMCP can offer it to an agent. Forms never set `toolautosubmit`: an agent fills the form and the person still sends it. When an agent does send it, the browser gets the outcome back, and the submission is marked "Sent by an AI assistant" in Sonor.
- **Nothing typed is lost.** The server-rendered form stays on the page until the interactive one has loaded. What was typed or autofilled carries across, focus stays in its field, and a Send pressed early is held and sent once the form is ready. The server form's Send button is disabled until it can catch a submit, and a visitor without JavaScript is told why the form won't send.
- **`submit()` returns the outcome.** From `useForm` and the form render props: `{ status: 'sent' | 'invalid' | 'failed' | 'busy' | 'next_step', ... }`. `useForm` also returns `handleSubmit` and `toolAttributes` for custom markup: `<form {...toolAttributes} onSubmit={handleSubmit}>`.
- `ServerForm`'s `enhance` prop is deprecated and ignored. Every server form upgrades to the interactive one.

See [Forms](https://sonor.dev/site-kit/forms).

### Popups wait for forms, and every popup is a named dialog

- A popup or toast that opens on its own (immediately, after a delay, on scroll or on exit intent) waits while the visitor is in the middle of a form: focus in a field, a filled-in field not yet sent, or a form that's sending. It opens a moment after they leave the form, or once it's sent. Banners still show at once.
- A popup is a modal dialog named by its heading: it takes focus, keeps Tab inside, closes on Escape and returns focus where it was. A toast is a named non-modal dialog and a bar is a named region. Close buttons are named "Close".

See [Popups and banners](https://sonor.dev/site-kit/popups).

### BookingWidget

- Day buttons are named with the full date, year included ("Wednesday, October 7, 2026"), and time buttons with the time and date in the booking's time zone. The picked day and time are pressed (`aria-pressed`). Confirm is named with what it confirms ("Confirm 10:30 AM, Wednesday, October 7, 2026"). Every name starts with what its button shows, so voice control still works. What the widget sends is unchanged.

See [Sync](https://sonor.dev/site-kit/booking).

### SEO and AI visibility

- **Template placeholders never reach the page.** `ManagedSchema`, `LLMSchema` and `generateAllArticleSchemas` drop placeholder nodes from Sonor-supplied schema: a URL on a reserved example domain, a name like "Example" or "Your Business Name", a placeholder phone, a slot like `[Resident Name]` or `{plan.name}`, or an object that's only a note. Real siblings and parents stay, and an article whose stored schema is nothing but placeholders gets its generated schema instead. Your own `additionalSchemas` are never touched. The rule is `@sonordev/contracts/schema-placeholders`.
- **llms.txt summaries end at a word.** A long page summary ends at a sentence or a whole word, never mid-word.

See [SEO](https://sonor.dev/site-kit/seo) and [Articles](https://sonor.dev/site-kit/articles).

### Also

- Built and tested against Next.js 16.3.8, the September 30 security release. Update `next` on each site; the peer range is unchanged.
- The 7.0 migration guide covers sonor-setup 7.1.2's proxy fix: on a `src/app` site the proxy belongs in `src/`.

## 7.1.2

Docs and comments only; no code changes.

- The module READMEs, `AGENTS.md`, the 7.0 migration guide and the code comments use fictional businesses and example.com in their examples, and describe spam protection without the detail of how it decides.
- Messages and comments say "Sonor" where they said "Portal", the product's old name. Identifiers are unchanged.
- This changelog starts at 7.0. Earlier releases are listed in the repository's `docs/CHANGELOG-BEFORE-7.md`.

## 7.1.1

- BookingWidget now asks every guest for a phone number before confirming a meeting, and sends it with the booking for CRM and Google Contacts use.

## 7.1.0

### Live updates (new)

- **`@sonordev/site-kit/revalidate`**: the route Sonor calls when content
  changes, as a one-line file. Pages stay cached, and an edit in Sonor
  (managed copy, metadata, an article, a portfolio item) regenerates only the
  pages and cache tags it touched, within seconds:

  ```ts
  // app/api/seo-revalidate/route.ts
  export { POST } from '@sonordev/site-kit/revalidate'
  ```

  `createRevalidateRoute({ publicationBasePath })` for a site with articles.
  It reads `SONOR_API_KEY` on every call, so there's no new secret. See
  [Live updates](https://sonor.dev/site-kit/live-updates).
- **Ping**: `{ "ping": true }` on its own regenerates nothing and answers
  `{ ok, ping, version }`, so Sonor can confirm the route is installed before
  it promises an edit will go live in seconds. `createSeoRevalidationHandler`
  answers it too, so existing routes built on it need no change.
- SEO fetches carry the `seo` cache tag and slot fetches take theirs from the
  shared list (`SITE_CACHE_TAGS`, `@sonordev/contracts/site-cache`), the
  names Sonor sends.
- The Managed copy docs pointed at a separate slots route with its own
  secret, which Sonor never called. They point at the one route now.

### Edit on page (new)

- Sonor's dashboard can open the live site with its managed copy outlined:
  click any of it to edit it, and see text, rich-text and link changes on the
  page before publishing. Nothing to install beyond 7.1: no login, no secret,
  no route. The overlay loads only when Sonor frames the page with
  `?sonor_edit`; every other visitor gets an unchanged page and about 40
  bytes of extra JavaScript. It finds copy by its visible text, so markup and
  CSS stay exactly as written, and it never writes anything itself.
- `DEFAULT_FRAME_ANCESTORS` adds `https://app.sonor.io`, so the dashboard
  can frame the site. A site that sets its own `frame-ancestors` should use
  the default list.

### Managed copy: rich text, links and lists

- **`<ManagedRichText>`**: paragraphs, bold, italic, links and lists, edited
  in Sonor and rendered as elements (never HTML). A string child is the
  fallback.
- **`<ManagedLink>`**: a label and a destination editable together; renders
  a plain `<a>`, or pass a render function for your own button.
- **`<ManagedList>`**: repeatable items (title, body, link, image) with your
  markup, e.g. a services grid or an FAQ.
- `getSlot(id, { type, fallbackValue })` resolves any of them without a
  component. A value is only used when its kind matches what the code
  renders, so changing a slot's kind in code falls back rather than breaking.
- Slots contract v2: the signature covers the structured value. Sonor still
  serves text to older site-kit releases.
- New docs page: [Managed copy](https://sonor.dev/site-kit/copy).
- List items take photos: Sonor uploads them with the project's files and
  records their size, so a gallery is a `<ManagedList>` (see "Lists with
  photos" in the Managed copy docs).

### Deprecated (removed in 8.0)

- **`./cms`, `./cms/server`, `./website/cms`, `./website/cms/server`**: the
  Sanity CMS is retired. No project had CMS pages and Sonor no longer serves
  them, so these render nothing. Use managed copy.
- **`<ManagedContent>` / `getManagedContentData`** (`./seo`): content blocks
  are retired for the same reason. Use `<ManagedRichText>`.

## 7.0.2

- The docs ship in the package: every module README, the migration guide and
  this changelog, listed in a new `docs.json`. [sonor.dev](https://sonor.dev)
  reads them straight from the published release, so the docs there always
  match `latest`.
- README fixes: a broken link to the articles docs, "Portal" where it meant
  Sonor, and a reCAPTCHA variable site-kit no longer reads.

## 7.0.1

- `reportToolCallsToSonor` sends the `x-sitekit-version` header every other
  site-kit request carries, so Sonor records which site-kit a tool call came
  from (its `kit_version` was empty).

## 7.0.0

A major: one entry per Sonor module, a root that runs nothing, ESM only,
Next 16 only, a 0.6 MB package, Cache Components support, and MCP tools
every site can give agents. Most sites move in one command when next
touched: `npx sonor-setup codemod --write`, then build. The full guide is
[docs/MIGRATING-TO-7.md](https://sonor.dev/site-kit/migrating-to-7). Copies of sites on 4.2,
5.8 and 6.5 were migrated that way and built.

### Agents: MCP tools for every site (new)

- **`@sonordev/site-kit/mcp/sonor`**: the tools sites used to hand-roll,
  written once over the fetchers the site's own pages use. `sonorMcpServer` /
  `sonorMcpTools`: `get_business_profile`, `list_services`, `search_faq`,
  `find_pages`, `list_articles`, `get_article`, `get_reviews`, plus opt-in
  `list_offerings`, `check_availability` and `get_inquiry_form` +
  `send_inquiry`. `send_inquiry` goes through Sonor's agent-inquiry door:
  the person must have said yes, and the call carries the agent's badge and
  `human_approved`.
- **Who called.** `createMcpHandler` takes `onToolCall`; `reportToolCallsToSonor`
  sends each call (tool, outcome, the agent's name, never the arguments) to
  Sonor after the response. The dashboard's AI Visibility tab and Echo show
  which agents used the site, and Echo offers the fix for a failing tool.
- **`npx sonor-setup mcp`** writes the whole wiring: server file, endpoint
  with reporting, the server card at both well-known paths, and on Netlify
  the rate-limited relay plus `MCP_TRANSPORT_SECRET`.
- **A custom MCP server is left alone.** A site that runs its own
  (a re-site-kit site, say) gets nothing automatic: `sonor-setup mcp`
  writes nothing there, and no llms.txt section is added. Every piece is
  opt-in for it (reporting, some built-in tools, the section). See
  src/mcp/README.md.
- **Discovery.** llms.txt gains an "Agent access" section (added to the
  build-time file automatically for the built-in server only; `mcp: false`,
  `createSitemap({ llmsAgentAccess })` or `--no-agent-access` turn it off), and
  `createProxy({ llmsDiscovery: { mcpServerCard: true } })` links the card as
  `rel="service-desc"`.

### Breaking

- **ESM only.** `"type": "module"`; each export is `{ types, default }`. Next
  and the kits are unaffected; Node 20.19+/22.12+ `require()` it, which
  covers a `next.config.ts`. `engines.node` is `>=20.19`.
- **Next 16 only** (`next` peer `^16`).
- **The root entry is types-only** (plus `SITE_KIT_VERSION`). `BookingWidget`
  → `./sync`, `AffiliatesWidget`/`useAffiliates` → the new `./affiliates`,
  commerce → `./commerce`, `ManagedImage` → `./website/images`, signal →
  `./signal`, reputation → `./reputation`, redirects → `./seo/redirects`,
  `LandingPage` → `./website/landing`; `formatBookingTime`/`formatBookingDate`
  are `formatTime`/`formatDate` on `./sync`. The codemod rewrites these.
- **The setup CLI is its own package, `sonor-setup`.** `npx sonor-setup`
  works unchanged; a site whose scripts run it adds it as a dev dependency
  (the codemod does). The `site-kit` bin alias is gone.
  `sonor-register-sitemap` stays here.
- **No source maps in the package**, and no `.d.ts.map`s.
- **Removed, no site used them:** `./search`, `./search/contract`,
  `./redirects/not-found` (with `x-sk-path` and `SK_PATH_HEADER`), `./setup`,
  `LocationPageContent`/`getLocationSection`, `identifyTopicClusters`,
  `formatContentSignals`, the `SiteKitConfig` type, and the postinstall GEO
  bootstrap (site-kit runs nothing at install; use `npx sonor-setup geo`).
- **The register-sitemap bin's .env precedence is Next.js's**: the real
  environment wins, then `.env.production.local`, `.env.local`,
  `.env.production`, `.env`. It used to let `.env.local` override a
  variable the host had set. `dotenv` is gone (Node's `util.parseEnv`).

### Smaller and lighter

- The tarball is **0.60 MB** (2.24 MB unpacked), from 4.03 MB (17 MB) in 6.x.
- **One markdown library, `marked`.** The chat widget renders its lexer
  tokens as React elements (raw HTML shows as text; only http(s), mailto,
  tel and relative links keep an address). react-markdown and its 85
  packages (\~8 MB) are gone from every site's install.

### New

- **Cache Components.** A site can turn on Next 16's `cacheComponents`:
  nothing in site-kit's render path reads the wall clock or `Math.random`
  any more (deadlines, TTLs and jitter use `performance.now()`; the forms'
  render stamp is set on mount). The integration harness builds the fixture
  both ways.
- **`@sonordev/contracts`**, a new dependency-free package: the rules
  site-kit shares with the Sonor APIs and the dashboard (popup blocks, site
  hosts, seo\_pages resolution, title quality, llms sanitizers, forms, fleet,
  slots, portfolio). site-kit's `*/contract` entries are the same code.
- **`sonor-setup codemod`** moves `middleware.ts` to `proxy.ts` (Next 16's
  rename) with `createMiddleware` → `createProxy`, deterministically and
  offline. It flags a file that sets `runtime` instead of moving it.

### Fixed

- The seo/website homes one directory deeper than their source shipped
  declarations that pointed at themselves (e.g. `seo/og/route` exported
  nothing to TypeScript); `./website/slots` dropped `./slots`' default
  export; `BookingWidget` and `TestimonialSection` could land in a
  non-client entry and fail a server page's prerender. verify-dts and the
  build now check all three.

### Kept through 7.x

- Every 6.6 path (`./sitemap`, `./og`, `./llms`, `./images`, `./engage`,
  `./middleware`, …) still works as an alias of its new home, marked
  deprecated in the agent manifest; so do `createMiddleware` and
  `SiteKitMiddlewareConfig`. They go in 8.0.
- `SiteKitLayout`'s `engage` prop, alongside `chat` and `popups`.
- Deprecated options sites still pass (`contentSignals`, `nativeReturnTo`,
  `ManagedScripts`) are no-ops until 8.0.
