npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@alxxsck/ai-assistant

v3.10.0

Published

Embeddable Lola AI assistant library

Readme

Lola AI Assistant

Embeddable browser library for the Lola assistant. The package owns its DOM host, Shadow DOM, chat UI, avatar, realtime transports, voice flow, and backend command runtime. A product frontend supplies only an authenticated interaction session and the few integrations that must cross into the host application.

Install and build

The package requires React 19 or newer as a peer dependency.

npm install
npm run build-lib

Library transport and asset origins are embedded at build time:

VITE_LOLA_API_URL=https://api.example.test/api/v1
VITE_LOLA_WS_URL=wss://ws.example.test/cable
VITE_AI_ASSISTANT_ASSETS_URL=https://assets.example.test

All three values are required absolute URLs. The library build fails when a value is missing, malformed, or uses an incompatible protocol. VITE_* values are public client configuration: never place service-account keys, access tokens, or other credentials in them. CI values override the placeholders in .env.

Minimal integration

The product backend creates a Lola customer interaction session. The product frontend passes the short-lived result to the library:

import { ChatWidgetInstance } from "@alxxsck/ai-assistant";

const widget = new ChatWidgetInstance({
  customerInteractionSession: {
    accessToken: session.accessToken,
    interactionSessionId: session.interactionSessionId,
    expiresAt: session.expiresAt,
  },
  locale: productLocale,
  onRenewSession: async () => {
    const nextSession = await productApi.createLolaInteractionSession();
    widget.setCustomerInteractionSession(nextSession);
  },
});

await widget.track({
  event: "Deposit Form Opened",
  params: { currency: "EUR" },
});

CustomerInteractionSession contains exactly:

type CustomerInteractionSession = {
  readonly accessToken: string;
  readonly interactionSessionId: string;
  readonly expiresAt: string;
};

Realtime settings, voice availability, provider voice selection, and project settings belong to Lola Backend. They are not widget constructor properties and must not be copied into the interaction session.

The instance creates a dedicated host below document.body and attaches an open Shadow DOM to it. Multiple instances remain isolated. Call destroy() to unmount the widget, disconnect its runtime, clear pending work, and remove the owned host.

Lifecycle

The supported instance methods are:

widget.setOpen(true);
widget.setLocale(productLocale);
widget.setCustomerInteractionSession(nextSession);
await widget.track({ event: "Game Opened", params: { gameId: "42" } });
widget.destroy();

setOpen may be called before asynchronous widget initialization completes; the latest requested state is applied when the UI is ready. Session replacement updates authenticated HTTP, realtime, message, voice, and command dependencies without recreating the public widget instance.

Compact scenario preview

Compact presentation is automatic and is not controlled by host configuration. When the chat is closed, a new eligible Scenario message, question, or CTA appears next to the launcher. When the chat is already open, or a Scenario explicitly opens it first, the same content is rendered only in the chat. Hiding the complete assistant chrome also hides and clears the compact surface.

Launcher docking and panel geometry use the widget's existing origin-scoped persistence. Host applications do not decide which presentation surface a Scenario uses.

Localization

Pass the product's current locale directly to the widget. English is used when locale is omitted, empty, invalid, or unsupported:

const widget = new ChatWidgetInstance({
  customerInteractionSession: session,
  locale: productLocale, // for example "es-MX" or "pt-BR"
});

When the product language changes, update the mounted widget in place:

widget.setLocale(nextProductLocale);

Regional tags are normalized to a supported base language. The widget currently ships ru, uz, en, it, es, de, fr, pl, pt, bn, sw, so, and ar; Arabic automatically enables right-to-left layout. Updating the locale preserves the open route, messages, draft, active conversation, voice state, and CTA state, and does not reconnect transports.

The locale controls only text owned by the widget. Assistant and backend content is displayed as received; the assistant's response language follows the user's messages. See the localization guide for the complete integration contract.

The widget calls onRenewSession before expiry and after supported authentication failures. Refresh through the product backend and replace the session in the callback:

const widget = new ChatWidgetInstance({
  customerInteractionSession: session,
  onRenewSession: async () => {
    const nextSession = await productApi.createLolaInteractionSession();
    widget.setCustomerInteractionSession(nextSession);
  },
});

No event subscription or listener cleanup is required. Parallel renewal signals are debounced, and replacing the session cancels a pending duplicate callback.

Scenario interaction observations never cross an interactionSessionId boundary. A token replacement for the same session retries the same observation IDs. Canonical renewal creates a new interaction session, so any unsent queue owned by the previous session is discarded and sequencing starts again. This is an intentional principal-isolation rule: old observations are never reauthenticated with a new session token.

Support identities and avatars

Support Presentations contract v1 uses responderPresentation schema v2. Message bubbles use the immutable public author snapshot, while the header renders this backend-owned projection. Its humanSupport and ai fields are independent: only a current Assignment can name an operator, while routing and ambiguous assignments stay generic Support. Operator photos are resolved through short-lived, conversation-scoped grants; signed URLs stay in memory and are purged on session replacement, authorization loss, and destroy().

Integrations do not pass names, avatar URLs, CMS user IDs, or support status into the constructor. These values come from Lola Backend. Older backends still render with safe fallbacks, but exact historical photos and responder presentation require CH-01 contract v1. See the support presentation guide for the wire contract, fallback rules, and failure behavior.

track sends product events directly to Lola Backend using the active interaction session. The library owns the Lola URL, bearer authentication, event timestamp, route context, and idempotency key. Tracking failures are reported through the standard widget logger and do not reject into the product flow. A session replaced with setCustomerInteractionSession is used by all later tracking calls.

The constructor and replacement method reject empty tokens, empty interaction session identifiers, and invalid expiry timestamps.

Launcher control

The built-in launcher is visible by default. Products that render their own button can hide it while keeping the same programmatic open path:

const widget = new ChatWidgetInstance({
  customerInteractionSession: session,
  hideOpenButton: true,
});

productButton.addEventListener("click", () => widget.setOpen(true));

Closing a hidden-launcher widget leaves it non-interactive until the product calls setOpen(true) again. The runtime and active interaction session remain mounted.

Attachments

Attachment support is enabled by default and uses Lola Backend's quarantine-and-scan pipeline. The picker and drag-and-drop target appear only when the current server-authoritative responder presentation is CURRENT and human support is ASSIGNED; they stay unavailable before a current human-support assignment exists, including NONE and ROUTING. If that assignment is released after a file was selected, the draft remains removable but is not sent while the assignment is absent. The SDK sends only attachments that reach READY; uploaded bytes never become durable message URLs. Set attachments.enabled to false only when embedding against a legacy backend.

const widget = new ChatWidgetInstance({
  customerInteractionSession: session,
  // Optional: attachments: { enabled: false },
});

The default adapter hashes the file, creates an upload intent, streams the bytes to the presigned storage URL with progress, completes the upload, and waits for the malware scan. Download and image/PDF preview grants are fetched only when the user asks for them. Signed URLs are never retained in chat history.

For controlled demos or tests, a custom transport can replace that adapter. READY attachment ids are sent by default; sendAttachmentIds: false is a temporary legacy-backend escape hatch.

attachments: {
  enabled: true,
  sendAttachmentIds: true,
  transport: {
    upload: async ({ file, conversationId, onProgress, onStage, signal }) => {
      // Create an upload session, PUT the file, then complete it.
      // Return durable metadata: { id, fileName, mediaType, size, kind }.
    },
    grant: async ({ attachmentId, conversationId }) => ({ url, expiresAt }),
    revoke: async ({ attachmentId, conversationId }) => {},
  },
}

Host handlers

handlers is optional and is the only command execution surface. Use it when a backend command must navigate the product, open a product modal, or customize an element highlight. uiActionPolicies separately controls trusted presentation behavior:

const widget = new ChatWidgetInstance({
  customerInteractionSession: session,
  uiActionPolicies: {
    account_page: { mobileDisposition: "collapseOnSuccess" },
    featured_game_page: { mobileDisposition: "immersive" },
  },
  handlers: {
    openPage: ({ route }) => productRouter.push(route),
    openModal: ({ modalName }) => productModals.open(modalName),
    highlight: ({ element, active, reportActivation }) => {
      element?.classList.toggle("lola-highlight", active);
      productHighlights.setAction(
        element,
        active
          ? () => {
              reportActivation();
              productActions.openDeposit();
            }
          : null,
      );
    },
  },
});

uiActionPolicies is trusted host configuration keyed by canonical action code. keep leaves the assistant surface open; collapseOnSuccess and immersive minimize it to the compact assistant after the handler resolves, on both desktop and mobile. mobileDisposition is retained as a compatibility field name; a value supplied in command payload is ignored. Unregistered page, modal, and element actions default to collapseOnSuccess.

Navigation and modal handlers should resolve only after the destination is ready to own focus, or reject on failure. A successful handoff never returns focus to Lola's launcher; it preserves host focus while manual collapse still returns focus to the assistant. immersive currently shares the safe surface-minimization semantics of collapseOnSuccess; hiding all assistant chrome is reserved until the host exposes an explicit destination-exit lifecycle.

reportActivation() is the authoritative signal that the product accepted the user activation. Call it synchronously from the product-owned action handler, before navigation can unmount the widget. It records the activation, not the result of the product action. The attributed target remains observable until its highlight is removed or replaced, including after the Scenario closes the chat. The SDK observes target visibility, but never treats arbitrary host DOM clicks as activation. Existing highlight handlers may ignore this callback.

Page and modal targets arrive in canonical Lola command envelopes. The built-in element fallback resolves stable [data-lola="..."] attributes and reports only visibility because it does not own the product action. CTA display, chat open/close, speech, voice conversations, and avatar animation stay internal.

Command lifecycle events are available as command_received, command_succeeded, command_failed, command_expired, and command_unsupported.

Diagnostics

Set debug: true to enable verbose lifecycle, realtime, speech, and voice logs prefixed with [Lola-Widget]. Initialization and runtime errors remain visible without this flag. The option does not restore the removed debug GUI or avatar debug controls.

Breaking migration

This release intentionally removes the former dev/demo and host-configuration surface. Remove these constructor properties from integrations:

  • mountElementId, agentId, customerId, configName
  • apiUrl, wsUrl
  • AIAvatar, background, footer, voiceEnabled, voiceConfig
  • manualOpen, avatarDebug, uiTargets, commandHandlers
  • compactScenarioPreview

Replace manualOpen with hideOpenButton: true only when the standard launcher must be hidden. Remove calls to playAnimation, executeReaction, sendServiceMessage, registerUiTarget, and registerCommandHandler. Backend commands retain the supported assistant behaviour internally. Remove compactScenarioPreview without replacement: the SDK automatically renders eligible Scenario content as a compact preview only while the chat is closed, and renders it in the full chat while the chat is open.

The repository ships only the library build. It no longer contains an app-mode HTML entry, local session creation, remote JSON widget configs, a debug GUI, preview/deploy demo scripts, or a Vite dev-server configuration.

Verification

npm run typecheck
npm test
npm run lint
npm run format:check
npm run build-lib