@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/chatand popups are@sonordev/site-kit/website/popups.SiteKitLayoutmountsSiteChatandSitePopupsas 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.
DesignRendererand its types are removed. - New:
SiteChatin./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/engagestill builds through 7.x.ChatWidgetand the chat types re-export from it, the popup types keep their old names (EngageElementisSitePopup), andEngageWidgetdrawsSiteChatandSitePopups. 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
autocompletetoken (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 withrole="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, sincerequiredalready says it. - WebMCP. The interactive form declares itself as a tool (
toolname,tooldescription, andtoolparamdescriptionon fields with help text), so a browser that supports WebMCP can offer it to an agent. Forms never settoolautosubmit: 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. FromuseFormand the form render props:{ status: 'sent' | 'invalid' | 'failed' | 'busy' | 'next_step', ... }.useFormalso returnshandleSubmitandtoolAttributesfor custom markup:<form {...toolAttributes} onSubmit={handleSubmit}>.ServerForm'senhanceprop 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,LLMSchemaandgenerateAllArticleSchemasdrop 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 ownadditionalSchemasare 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.
Also
- Built and tested against Next.js 16.3.8, the September 30 security release. Update
nexton each site; the peer range is unchanged. - The 7.0 migration guide covers sonor-setup 7.1.2's proxy fix: on a
src/appsite the proxy belongs insrc/.
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 readsSONOR_API_KEYon 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.createSeoRevalidationHandleranswers it too, so existing routes built on it need no change. -
SEO fetches carry the
seocache 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_ANCESTORSaddshttps://app.sonor.io, so the dashboard can frame the site. A site that sets its ownframe-ancestorsshould 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.
- 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 matchlatest. - 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
reportToolCallsToSonorsends thex-sitekit-versionheader every other site-kit request carries, so Sonor records which site-kit a tool call came from (itskit_versionwas 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-inlist_offerings,check_availabilityandget_inquiry_form+send_inquiry.send_inquirygoes through Sonor's agent-inquiry door: the person must have said yes, and the call carries the agent's badge andhuman_approved.- Who called.
createMcpHandlertakesonToolCall;reportToolCallsToSonorsends 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 mcpwrites the whole wiring: server file, endpoint with reporting, the server card at both well-known paths, and on Netlify the rate-limited relay plusMCP_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 mcpwrites 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-accessturn it off), andcreateProxy({ llmsDiscovery: { mcpServerCard: true } })links the card asrel="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 anext.config.ts.engines.nodeis>=20.19. - Next 16 only (
nextpeer^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/formatBookingDateareformatTime/formatDateon./sync. The codemod rewrites these. - The setup CLI is its own package,
sonor-setup.npx sonor-setupworks unchanged; a site whose scripts run it adds it as a dev dependency (the codemod does). Thesite-kitbin alias is gone.sonor-register-sitemapstays here. - No source maps in the package, and no
.d.ts.maps. - Removed, no site used them:
./search,./search/contract,./redirects/not-found(withx-sk-pathandSK_PATH_HEADER),./setup,LocationPageContent/getLocationSection,identifyTopicClusters,formatContentSignals, theSiteKitConfigtype, and the postinstall GEO bootstrap (site-kit runs nothing at install; usenpx 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.localoverride a variable the host had set.dotenvis gone (Node'sutil.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 orMath.randomany more (deadlines, TTLs and jitter useperformance.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*/contractentries are the same code.sonor-setup codemodmovesmiddleware.tstoproxy.ts(Next 16's rename) withcreateMiddleware→createProxy, deterministically and offline. It flags a file that setsruntimeinstead of moving it.
Fixed
- The seo/website homes one directory deeper than their source shipped
declarations that pointed at themselves (e.g.
seo/og/routeexported nothing to TypeScript);./website/slotsdropped./slots' default export;BookingWidgetandTestimonialSectioncould 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 docreateMiddlewareandSiteKitMiddlewareConfig. They go in 8.0. SiteKitLayout'sengageprop, alongsidechatandpopups.- Deprecated options sites still pass (
contentSignals,nativeReturnTo,ManagedScripts) are no-ops until 8.0.