@burdenoff/website-sdk 2026.1101.4 → 2026.1102.2

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/README.md CHANGED
@@ -298,8 +298,9 @@ publishes audio, video or data, and it sends the avatar nothing to say.
298
298
  live: {
299
299
  site: 'healthybowl',
300
300
  load: () => import('@burdenoff/website-sdk/avatar-live'),
301
- turnstileSiteKey: TURNSTILE_SITE_KEY,
302
301
  manifestVersion: 'hb-en-1', // the version of `suggestedQuestions`
302
+ // Optional: used only where the server checks for bots (see "The bot check is the server's setting").
303
+ turnstileSiteKey: TURNSTILE_SITE_KEY,
303
304
  },
304
305
  }}
305
306
  />
@@ -339,7 +340,24 @@ publishes audio, video or data, and it sends the avatar nothing to say.
339
340
  - **Needs the panel under an `ExploreChatProvider`.** The live avatar reads the
340
341
  provider's conversation. Without a provider, on a shared (read-only)
341
342
  conversation, in a locale whose strings have not been reviewed yet (English
342
- only, for now), or with an incomplete `live`, the Avatar view stays as it was.
343
+ only, for now), or with an incomplete `live` (no `site`, `load` or
344
+ `manifestVersion`, or a `turnstileSiteKey` or `allowTestSiteKey` of the wrong
345
+ shape), the Avatar view stays as it was.
346
+ - **The bot check is the server's setting (web API contract v1.2, additive).**
347
+ The availability answer says whether the server checks for bots when a
348
+ session is created: `"botCheck": {"mode": "off"}` or
349
+ `{"mode": "turnstile"}`. With `off`, Start loads the room module and creates
350
+ the session, and nothing else: no Cloudflare script, no widget, no WebCrypto
351
+ needed and no `x-web-avatar-bot-token` header; the server's own guards and
352
+ caps decide. With `turnstile`, Start runs the Cloudflare Turnstile check with
353
+ the page's own `turnstileSiteKey`, as before; without a key the page may use
354
+ (none, or one of Cloudflare's test keys where `allowTestSiteKey` is `false`)
355
+ the Avatar view stays as it was. A server older than v1.2 sends no `botCheck`
356
+ and always checks, so it is read as `turnstile`. Any other `botCheck` (an
357
+ unknown mode, another field, another shape) leaves the view as it was. So
358
+ turning the bot check on or off is a server config change: a site needs no
359
+ new build for it, as long as its build carries a site key for `turnstile` and
360
+ its CSP allows Cloudflare (below).
343
361
  - **One answer: the first, to a suggested question.** The avatar service
344
362
  accepts a conversation only when it holds exactly two messages: the first user
345
363
  message (a suggested question) and the first assistant answer, completed. The
@@ -350,14 +368,23 @@ publishes audio, video or data, and it sends the avatar nothing to say.
350
368
  asked again without Retry), it asks the visitor to start a new chat and choose
351
369
  a suggested question. Whether the first question really is one of the
352
370
  suggested ones is the service's to judge (`not_eligible`).
371
+ - **A fresh answer only.** The service also refuses an answer more than fifteen
372
+ minutes old (from the answer's `createdAt`): a conversation restored from the
373
+ browser's history, say. The view stops offering an answer fourteen minutes
374
+ after its `createdAt` (a minute's margin; a missing or unreadable time counts
375
+ as too old), also while the visitor is on the consent card: one timer moves
376
+ the card to the start-over step at that moment. The start-over step and the
377
+ `not_eligible` refusal both have a **New chat** button, which is the chat's
378
+ own New chat (the shared conversation's `reset`) followed by the Chat tab; no
379
+ host prop is needed for it.
353
380
  - **What the visitor sees.** Opening the Avatar view asks the gateway (the
354
381
  origin of your configured `gatewayUrl`, at `/api/web-avatar/v1/availability`)
355
382
  whether the live avatar is offered for this site and locale. When it is and
356
383
  the conversation has its first answer, a consent card explains what happens
357
384
  and links to `/privacy#live-avatar`; with no answer yet, the view asks the
358
385
  visitor to choose a suggested question in Chat first. Start runs a
359
- Cloudflare Turnstile check (action `avatar_start`), creates the session and
360
- joins its room: the video, the captions, an "AI avatar · not a real person"
386
+ Cloudflare Turnstile check (action `avatar_start`) where the server checks
387
+ for bots, creates the session and joins its room: the video, the captions, an "AI avatar · not a real person"
361
388
  badge, a countdown, Stop and Continue in Chat. Where a browser holds sound
362
389
  back (Safari, iOS), a "Tap to turn on sound" button appears. A refusal (busy,
363
390
  limits, region, language, paused, a question the avatar cannot read) is one
@@ -379,6 +406,14 @@ publishes audio, video or data, and it sends the avatar nothing to say.
379
406
  digest for all of them (the card's title, intro, processing, storage and
380
407
  privacy-link lines, each ending with a line feed: 760 bytes, SHA-256
381
408
  `2e02b051ceb1163c14bc58fa63a954a12c0d1f2695029a6d4805922d74c793f7`).
409
+ `web-consent-2026-11-03` is the same card without "When you start,
410
+ Cloudflare checks for bots.", for a server whose bot check is `off` (716
411
+ bytes, SHA-256
412
+ `bb7516df3cf3a910c6ec1623391f859e6c48e7e7d305d4fb333f18d4115da203`).
413
+ Each version says which bot check its words describe, and the card is shown
414
+ only where the server's `botCheck` is the same: a server that runs no bot
415
+ check must name `web-consent-2026-11-03`, and one that checks must name a
416
+ version that says so, or the view stays as it was.
382
417
  `web-consent-2026-10-31` (which names the product, "a suggested Healthy Bowl
383
418
  question") and `web-consent-2026-11` stay, unchanged, for servers that still
384
419
  name them.
@@ -389,22 +424,24 @@ publishes audio, video or data, and it sends the avatar nothing to say.
389
424
  apps-first hand-over, so it does not say which.
390
425
  - **Requests.** All three go to the gateway without credentials. The create
391
426
  request carries `content-type: application/json`, `idempotency-key` (a UUID
392
- v4) and `x-web-avatar-bot-token` (the Turnstile token, which the gateway
393
- verifies and does not forward); the availability request and the end request
394
- carry no header of the SDK's own. The conversation token is sent in the create
427
+ v4) and, only where the server checks for bots, `x-web-avatar-bot-token` (the
428
+ Turnstile token, which the gateway verifies and does not forward); the
429
+ availability request and the end request carry no header of the SDK's own. The conversation token is sent in the create
395
430
  body only. Neither it, nor the session's end secret, nor the room token, nor
396
431
  the bot check's token is ever written into the page, the console or storage by
397
432
  the live avatar (the chat keeps its own copy of the conversation in
398
433
  `localStorage`, as it always has).
399
- - **Bot-check binding (web API contract v1.1, additive).** The Turnstile widget
434
+ - **Bot-check binding (web API contract v1.1, additive; `turnstile` mode
435
+ only).** The Turnstile widget
400
436
  is rendered with `cData` set to
401
437
  `base64url_nopad(SHA-256(UTF-8("boff-web-avatar-cdata/v1\n" + site + "\n" + locale + "\n" + conversationToken + "\n" + messageId)))`,
402
438
  made with WebCrypto from the exact values the create body then carries, before
403
439
  the widget is rendered, so the gateway can check that the token was minted for
404
440
  this request. The action stays `avatar_start`, and nothing else in v1
405
- changes. A browser without WebCrypto (a page that is not a secure context)
406
- cannot start the live avatar: Start loads neither the room module nor the bot
407
- check's script there, and shows the view as it was. Pinned vector: site
441
+ changes. Where the server checks for bots, a browser without WebCrypto (a
442
+ page that is not a secure context) cannot start the live avatar: Start loads
443
+ neither the room module nor the bot check's script there, and shows the view
444
+ as it was. Pinned vector: site
408
445
  `healthybowl`, locale `en`, conversation token
409
446
  `ct_vector_0123456789abcdefghijklmnopqrstuvwx`, message id
410
447
  `11111111-2222-4333-8444-555555555555` give
@@ -418,7 +455,9 @@ publishes audio, video or data, and it sends the avatar nothing to say.
418
455
  preflight.
419
456
  - **Your page's headers.** A site with a Content Security Policy must allow:
420
457
  - `script-src` and `frame-src`: `https://challenges.cloudflare.com` (the
421
- Turnstile bot check);
458
+ Turnstile bot check). Only needed where the server's bot check is
459
+ `turnstile`; keep it anyway, so that turning the bot check on later is a
460
+ server change and not a new release of the site;
422
461
  - `connect-src`, the gateway origin (the three requests);
423
462
  - `connect-src`, the media room: `wss://<project>.livekit.cloud`. For a
424
463
  LiveKit Cloud room the media library also asks
@@ -587,14 +626,23 @@ A site adds these lines.
587
626
  usual place whenever the banner is not on screen.
588
627
 
589
628
  4. **The build env var** `VITE_AVATAR_TURNSTILE_SITE_KEY`: the site's
590
- Cloudflare Turnstile site key. `websiteAvatarLive` returns `undefined` (the
591
- still picture and "Live avatar coming soon", no live avatar) when it is
592
- unset or empty; on alpha (`VITE_DEPLOY_ENV=alpha`) it accepts Cloudflare's
593
- always-pass test key `1x00000000000000000000AA` (the alpha gateway runs
594
- Turnstile in dummy mode) or a real key; everywhere else, production
595
- included, only a real-looking key (`0x…`, letters, digits, `_` and `-`, 18
596
- to 64 characters) that is not one of Cloudflare's test keys (`1x…AA`,
597
- `2x…AB`, `1x…BB`, `2x…BB`, `3x…FF`, or anything of their shape). Install
629
+ Cloudflare Turnstile site key, used only where the server checks for bots
630
+ (its availability's `botCheck` is `turnstile`). The server decides whether
631
+ the live avatar is offered on the site (its reviewed site list, its pause,
632
+ its caps) and whether it checks for bots, so the key is no longer the
633
+ opt-in: when it is unset or empty, `websiteAvatarLive` returns the live
634
+ config without a key, which runs where the server's bot check is `off` and
635
+ stays the still picture where it is `turnstile`. On alpha
636
+ (`VITE_DEPLOY_ENV=alpha`) it accepts Cloudflare's always-pass test key
637
+ `1x00000000000000000000AA` (the alpha gateway runs Turnstile in dummy mode)
638
+ or a real key; everywhere else, production included, only a real-looking key
639
+ (`0x…`, letters, digits, `_` and `-`, 18 to 64 characters) that is not one of
640
+ Cloudflare's test keys (`1x…AA`, `2x…AB`, `1x…BB`, `2x…BB`, `3x…FF`, or
641
+ anything of their shape). A key that is set but refused returns `undefined`
642
+ (the still picture and "Live avatar coming soon"), never a live avatar
643
+ without it. It also sets `allowTestSiteKey` (true on alpha only), so the
644
+ view itself refuses a test key on any other build. Pass `enabled: false` to
645
+ keep the still picture on a build whatever the server offers. Install
598
646
  `livekit-client` on the site (see [Live avatar](#live-avatar-opt-in-receive-only)),
599
647
  and declare the variable in `vite-env.d.ts`.
600
648
  `websiteAvatarLive` trusts `VITE_DEPLOY_ENV`, so a production build must
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { E as ExplorePanelAvatarLive, a as ExploreLocale, A as AvatarConversation } from '../explore-avatar-live-config-BGsGwbcq.mjs';
3
+ import { E as ExplorePanelAvatarLive, a as ExploreLocale, A as AvatarConversation } from '../explore-avatar-live-config-Bp3kQbU2.mjs';
4
4
  import '../types-CndbmBK-.mjs';
5
5
  import '../use-explore-chat-zRuoAin6.mjs';
6
6
  import '../explore-types-Bb2Ih3d8.mjs';
@@ -23,6 +23,11 @@ interface AvatarLiveViewProps {
23
23
  onContinueInChat: () => void;
24
24
  /** "Continue in Chat" in the panel's language: the panel's own word for it, so this chunk carries none of the panel's. */
25
25
  continueInChat: string;
26
+ /**
27
+ * "New chat" in the panel's language, the panel's own word for it: the button that starts over (the conversation's
28
+ * `reset`, as the chat's own New chat button does, then the Chat tab) where this conversation can never be read.
29
+ */
30
+ newChat: string;
26
31
  /**
27
32
  * Nothing to ask the server about: an incomplete config, no gateway, no shared conversation to read (no provider, or a
28
33
  * read-only shared one), or a locale whose strings have not been reviewed. The tab stays as it was. Decided by
@@ -37,6 +42,6 @@ interface AvatarLiveViewProps {
37
42
  /** The panel's conversation, null when there is no provider. */
38
43
  conversation: AvatarConversation | null;
39
44
  }
40
- declare function AvatarLiveView({ live, active, locale, productName, still, fallback, onContinueInChat, continueInChat, off, base, headers, conversation, }: AvatarLiveViewProps): react.JSX.Element;
45
+ declare function AvatarLiveView({ live, active, locale, productName, still, fallback, onContinueInChat, continueInChat, newChat, off, base, headers, conversation, }: AvatarLiveViewProps): react.JSX.Element;
41
46
 
42
47
  export { AvatarLiveView, type AvatarLiveViewProps };
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { E as ExplorePanelAvatarLive, a as ExploreLocale, A as AvatarConversation } from '../explore-avatar-live-config-B171o1Yo.js';
3
+ import { E as ExplorePanelAvatarLive, a as ExploreLocale, A as AvatarConversation } from '../explore-avatar-live-config-Cetx8-Zu.js';
4
4
  import '../types-CndbmBK-.js';
5
5
  import '../use-explore-chat-DunjG-3p.js';
6
6
  import '../explore-types-Bb2Ih3d8.js';
@@ -23,6 +23,11 @@ interface AvatarLiveViewProps {
23
23
  onContinueInChat: () => void;
24
24
  /** "Continue in Chat" in the panel's language: the panel's own word for it, so this chunk carries none of the panel's. */
25
25
  continueInChat: string;
26
+ /**
27
+ * "New chat" in the panel's language, the panel's own word for it: the button that starts over (the conversation's
28
+ * `reset`, as the chat's own New chat button does, then the Chat tab) where this conversation can never be read.
29
+ */
30
+ newChat: string;
26
31
  /**
27
32
  * Nothing to ask the server about: an incomplete config, no gateway, no shared conversation to read (no provider, or a
28
33
  * read-only shared one), or a locale whose strings have not been reviewed. The tab stays as it was. Decided by
@@ -37,6 +42,6 @@ interface AvatarLiveViewProps {
37
42
  /** The panel's conversation, null when there is no provider. */
38
43
  conversation: AvatarConversation | null;
39
44
  }
40
- declare function AvatarLiveView({ live, active, locale, productName, still, fallback, onContinueInChat, continueInChat, off, base, headers, conversation, }: AvatarLiveViewProps): react.JSX.Element;
45
+ declare function AvatarLiveView({ live, active, locale, productName, still, fallback, onContinueInChat, continueInChat, newChat, off, base, headers, conversation, }: AvatarLiveViewProps): react.JSX.Element;
41
46
 
42
47
  export { AvatarLiveView, type AvatarLiveViewProps };