docs
    site-kit: Changelog
    v7.2.0.md

    @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 and Popups and banners.

    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.

    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.

    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.

    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 and 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:

      // 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.

    • 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.
    • <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.
    • 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 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. 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.maps.
    • 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.