@~lyre/intelligence-sdk
v0.7.0
Published
Embeddable Axis Intelligence sidebar SDK — a push-layout companion workspace (Web Components, SSR-safe) for inbox/support/campaign apps like axis-client.
Maintainers
Readme
@~lyre/intelligence-sdk
Embeddable Axis Intelligence sidebar — a push-layout companion workspace for inbox /
support / campaign apps. Native Web Components (Shadow DOM), SSR-safe, no React/Vue/Svelte
required. The first host is our controlled axis-client.
It is not a chatbot bubble: opening it in push mode takes real horizontal space and shrinks the host content, like a docked companion panel. Over time it surfaces insights, sentiment, agent coaching, FAQs, campaign recommendations and more.
Install
pnpm add @~lyre/intelligence-sdk # or: npm i / yarn add
# local/monorepo: "@~lyre/intelligence-sdk": "file:../intelligence-sdk"Quick start
import { createAxisIntelligence } from '@~lyre/intelligence-sdk';
const intelligence = createAxisIntelligence({
tenantId: '250', // Axis workspace id
appId: 'axis-client',
apiBaseUrl: 'https://intelligence.myaxis.ai',
mode: 'push', // 'push' | 'overlay' | 'inline'
side: 'right',
width: 420,
container: '#axis-app-shell', // host shell that should shrink
auth: { strategy: 'existing-session' }
});
intelligence.mount(); // browser-only; SSR no-opCustom-element registration (optional, for <axis-intelligence-panel> etc.):
import { defineCustomElements } from '@~lyre/intelligence-sdk';
defineCustomElements();Public API
createAxisIntelligence(config) → instance:
| Method | Description |
|---|---|
| mount() | Render the sidebar + trigger (browser only; SSR no-op). Idempotent. |
| unmount() | Remove everything and restore the host layout. |
| open() / close() / toggle() | Control the panel. |
| isOpen() | Current state. |
| updateConfig(partial) | Live-update mode/side/width/theme/auth/tenant. |
| on(event, handler) → off | Subscribe; returns an unsubscribe function. |
Events: mounted, unmounted, opened, closed, toggled, message:sent,
message:received, insight:selected, error.
Config
| Key | Default | Notes |
|---|---|---|
| tenantId, appId, apiBaseUrl* | — | required |
| mode | push | push | overlay | inline |
| side | right | left | right |
| width / minWidth / maxWidth | 420 / 320 / 640 | width is clamped |
| container | — | selector or element the push strategy shrinks |
| auth | {strategy:'existing-session'} | existing-session (cookies) | bearer-token | custom |
| initiallyOpen, debug | false | |
| theme | — | accentColor, radius, zIndex |
Modes
- push (primary): reserves space by adding padding on the configured side of the
container(the app shell), so host content shrinks. The sidebar is fixed-positioned in the freed gap. All mutated styles are snapshotted and restored on close/unmount — the host's inline styles are never permanently changed. - overlay: floats above content; host layout untouched (fallback).
- inline: renders inside
container(relative flow); no app-wide push.
Plug-and-play integration (Nuxt / Next / SvelteKit)
The SDK is framework-agnostic (vanilla custom elements) and SSR-safe. One call —
embedIntelligence(...) — wires everything; the host supplies only irreducible glue
(getToken, optional getHostContext, optional onNavigate). The SDK itself owns:
- Availability gate — probes
GET /api/intelligence/healthon mount and renders only if it can connect. If not, it stays fully dormant: no sidebar/icon, no telemetry, no API calls. Emitsavailable/unavailable;isAvailable()reflects the state. - Circuit-breaker — if a call later returns 401/403/404 (token revoked, app disabled, gone), it tears the UI down and stops telemetry instead of retrying forever.
- Token caching — return
{ token, expiresAt }fromgetToken; the SDK caches + refreshes. - Telemetry — sessions, page views, clicks, presence (batched, keepalive) — gated by the above.
- Route-aware context — re-syncs the iframe on SPA navigation automatically; no host watcher.
import { embedIntelligence } from '@~lyre/intelligence-sdk';
const instance = embedIntelligence({
appId: 'my-app',
apiBaseUrl: 'https://intelligence.myaxis.ai',
container: '#app-shell',
getToken: async () => {
const { session_id, expires_at } = await fetchAccountsToken();
return { token: session_id, expiresAt: expires_at }; // cached by the SDK
},
getHostContext: () => ({ workspaceName, user: { email } }), // pulled on each sync
onNavigate: ({ href, external }) => (external ? location.assign(href) : router.push(href)),
});
// later (HMR / unmount): instance.unmount();Call it from a browser-only entry point in each framework — full examples in
examples/:
- Nuxt — an
app/plugins/*.client.tsplugin →examples/nuxt.client.ts - Next.js (App Router) — a
'use client'component withuseEffect→examples/next.tsx - SvelteKit —
onMountin+layout.svelte→examples/sveltekit.svelte
Disable the gate with preflight: false (renders immediately, synchronously). Use
createAxisIntelligence(config) directly if you need full control over the config object.
SSR notes
- Importing the package touches no browser globals (custom-element classes extend a
guarded base, so
HTMLElementis never referenced at import). createAxisIntelligence()is safe server-side;mount()/defineCustomElements()no-op (with a debug warning) whenwindow/customElementsare absent.
Theming
Override CSS variables on the element or :host:
--axis-intelligence-width, --axis-intelligence-bg, --axis-intelligence-fg,
--axis-intelligence-muted, --axis-intelligence-border, --axis-intelligence-accent,
--axis-intelligence-radius, --axis-intelligence-z-index.
API client & mock fallback
The client calls the intelligence service: GET /api/intelligence/insights,
GET /api/intelligence/recommendations, POST /api/intelligence/ask. Every call degrades
gracefully — on network/auth/CORS failure it returns empty insights/suggestions and a local
chat fallback, so the embedded sidebar always renders. Endpoints derive from apiBaseUrl.
Build / test
npm run build # tsup → dist/ (ESM + .d.ts + IIFE global)
npm run typecheck # tsc --noEmit
npm run test # vitest (config, events, lifecycle, push-layout, SSR-safety)Accessibility
Sidebar uses role="complementary"; tabs use role="tab/tablist/tabpanel"; close/trigger
have accessible labels; Escape closes; the composer is focused on open; visible focus rings.
Cross-origin auth (live insights)
existing-session works cross-origin once the intelligence service is configured for it:
- Service side (already wired): set
INTELLIGENCE_CORS_ORIGINSto the host origin (e.g.http://localhost:3000). The service then answers CORS preflight, echoes the allowed origin withAccess-Control-Allow-Credentials: true, and issues the session cookie asSameSite=None; Secure(accepted onhttp://localhost). - SDK side: requests use
credentials: 'include'. On a401the sidebar shows a “Connect Axis Intelligence” button that opens the service's/auth/loginin a popup; when it closes, live insights reload. No 401 → empty states; never a dead end. - Alternatively use
auth: { strategy: 'bearer-token', token }to skip cookies entirely.
Limitations / roadmap
- Per-tenant authorization is not yet enforced by the service (any authenticated user can query any workspace id) — a service-side follow-up.
- Standalone
<axis-intelligence-sidebar>auto-init from HTML attributes is not wired yet — usecreateAxisIntelligence()(programmatic) for now. - No resize handle yet; width is configured. Insights/Suggestions are list views (no detail
drill-in beyond the
insight:selectedevent). - Roadmap: agent-coaching & playbook tabs, weekly digest, streaming chat, resize handle,
split into
@myaxis/intelligence-core+@myaxis/intelligence-ui.
See also:
SURFACE_ANCHOR_MODEL.mdfor the proposed framework-agnostic surface/anchor/render model that supports Nuxt, Next, Svelte, and plain web hosts.
Service configuration for embedding
INTELLIGENCE_CORS_ORIGINS=<axis-client origin>on the intelligence service (enables CORS- cross-site
SameSite=None; Securesession). Done.
- cross-site
- Still open: per-tenant authorization on the service.
