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

@bymos/agentkit-sdk

v0.8.0

Published

AgentKit embeddable widget — CDN build and npm package

Downloads

1,208

Readme

@bymos/agentkit-sdk

Embeddable AI chat widget for KlicForge. Add a fully-featured, themeable chat widget to any website or app — via a <script> tag or npm.

Installation

npm install @bymos/agentkit-sdk
# or
pnpm add @bymos/agentkit-sdk

Peer dependency: preact@^10.0.0


Quick start

Both integration paths take the same single-argument config.

CDN (no build step)

<script src="https://unpkg.com/@bymos/[email protected]/agentkit-widget.iife.js"></script>
<script>
  window.AgentKit.init({
    agentId: 'your-agent-id',
    tenantId: 'your-tenant-id',
    apiBaseUrl: 'https://api.klicforge.ai',
    title: 'Support',
  });
</script>

The @0.7 pin tracks the latest patch of that minor line. Pin an exact version (@0.7.0) if you need byte-identical builds, and add SRI (integrity + crossorigin="anonymous") when you do — an SRI hash only holds against an exact version.

npm (ESM / CJS)

import { init } from '@bymos/agentkit-sdk';

const widget = init({
  agentId: 'your-agent-id',
  tenantId: 'your-tenant-id',
  apiBaseUrl: 'https://api.klicforge.ai',
});

widget.on('message:received', (msg) => console.log(msg.content));

init and mount default their renderer and stylesheet to the bundled UI. To supply your own UI layer, pass them explicitly — init(config, myRenderer, myCss) — which is also the pre-0.6 call shape and still works unchanged.


Before it will run: allow your domain

The API rejects widget traffic from any origin that is not on the agent's allowlist. In the KlicForge dashboard open your agent → ChannelsWidget and add every origin the widget loads from (https://example.com, https://www.example.com — scheme and host must match exactly; there are no wildcards). Without this the widget renders an access-restricted panel instead of a chat.


Configuration

type AgentKitWidgetConfig = {
  // Required
  agentId: string; // Agent ID or slug from your KlicForge dashboard
  apiBaseUrl: string; // Base URL of your KlicForge API

  // Strongly recommended — required for conversation tracking and validation
  tenantId?: string;

  // Identity of the end user in your app (all optional; omit for anonymous)
  user?: {
    externalId?: string; // Your own identifier — DB ID, UUID, email, anything
    authId?: string; // ID from your auth system (JWT sub, Clerk userId, …)
    name?: string;
    email?: string;
  };
  sessionId?: string; // Resume a conversation across tabs/page loads

  // Display (optional)
  mode?: 'floating' | 'inline'; // Default: 'floating'
  title?: string; // Default: 'AI Assistant'
  subtitle?: string; // Shown under the agent name in the header
  description?: string; // Shown in the empty state
  avatarUrl?: string;

  // Behaviour (optional)
  streaming?: boolean; // Default: true
  metadata?: Record<string, unknown>; // Sent with every message

  // Theming (optional)
  theme?: {
    mode?: 'light' | 'dark' | 'system'; // Default: 'light'
    accentColor?: string; // Default: '#6366f1'
    fontFamily?: string; // Default: system UI stack
    borderRadius?: string; // Default: '12px'
  };
};

Config is validated at init — an invalid value throws a ZodError, so wrap init() in a try/catch if the values are user-supplied.

At init time the widget fetches the widget config set for your agent in the KlicForge dashboard (title, avatar, suggested questions, attachment limits) and uses it as defaults. Anything you pass here overrides it.

Suggested questions, photo/file upload and attachment limits are server-only — configure them on the agent in the dashboard; they cannot be set from the host page.

Your agent always serves its published version. preview, skipServerConfigFetch and configChannel are dashboard-internal: they only do anything for a caller holding a KlicForge session cookie for the owning tenant, so setting them on a public site has no effect.


Widget instance API

init(), mount() and mountFloating() all return a WidgetInstance:

widget.open();
widget.close();
widget.toggle();
widget.sendMessage('Hello!');
widget.sendMessage('See this', [{ file, kind: 'image' }]);
widget.reset(); // Clear the conversation and start over
widget.destroy(); // Tears down the DOM and closes the event stream
widget.getState(); // → WidgetState
widget.on(event, fn);
widget.off(event, fn);

Call destroy() when unmounting in an SPA — it closes the operator-reply event stream, which otherwise reconnects indefinitely.


Events

| Event | Payload | | ---------------------- | ------------------------ | | ready | WidgetState | | open / close | — | | message:sent | WidgetMessage | | message:received | WidgetMessage | | message:error | { error, message } | | conversation:started | { sessionId } | | stream:start | — | | stream:delta | { delta, accumulated } | | stream:end | { content } | | control_mode_changed | { mode } | | reset | — | | destroy | — |

control_mode_changed fires when a human operator takes over the conversation or hands it back to the agent.


CDN global API (window.AgentKit)

| Method | Description | | ------------------------ | ---------------------------------------------------- | | init(config) | Create a floating widget anchored to document.body | | mount(element, config) | Mount a widget inline inside a DOM element | | destroy(id?) | Destroy widget by ID, or all widgets if no ID given |

Inline mount example

<div id="chat"></div>
<script src="https://unpkg.com/@bymos/[email protected]/agentkit-widget.iife.js"></script>
<script>
  window.AgentKit.mount(document.getElementById('chat'), {
    agentId: 'your-agent-id',
    tenantId: 'your-tenant-id',
    apiBaseUrl: 'https://api.klicforge.ai',
  });
</script>

mount() always renders inline — there is no need to pass mode.


npm exports

// Lifecycle
import { init, mount, mountFloating, destroy } from '@bymos/agentkit-sdk';
import { destroyWidget, listWidgets, createWidget } from '@bymos/agentkit-sdk';

// Config
import { normalizeConfig } from '@bymos/agentkit-sdk';

// Session
import { getSessionId, setSessionId, clearSession } from '@bymos/agentkit-sdk';

// UI (requires the preact peer dep)
import { renderWidget, buildWidgetCSS, buildThemeVars } from '@bymos/agentkit-sdk';

// Lower-level
import { ApiClient, streamChat, EventEmitter } from '@bymos/agentkit-sdk';
import {
  createShadowHost,
  createFloatingContainer,
  prepareInlineContainer,
} from '@bymos/agentkit-sdk';

// Types
import type {
  AgentKitWidgetConfig,
  NormalizedWidgetConfig,
  WidgetInstance,
  WidgetState,
  WidgetMessage,
  WidgetEventMap,
  RenderFn,
} from '@bymos/agentkit-sdk';

mountFloating(element, config) anchors the launcher inside a specific element rather than the page — useful for previews and bounded layouts.


Session management

Within a browser tab, sessions are automatic — the SDK reads and writes sessionStorage keyed by agentId with no configuration needed. Sessions are cleared when the tab closes, and storage access is failure-tolerant: in Safari private mode or a partitioned third-party context the widget simply starts a new conversation on each load rather than erroring.

To persist a conversation across tabs or page reloads, save and restore the session ID yourself:

widget.on('conversation:started', ({ sessionId }) => {
  localStorage.setItem('chat-session', sessionId);
});

// Later, on the next load:
const widget = window.AgentKit.init({
  agentId: 'your-agent-id',
  tenantId: 'your-tenant-id',
  apiBaseUrl: 'https://api.klicforge.ai',
  sessionId: localStorage.getItem('chat-session') ?? undefined,
});

Theming

The widget renders inside a Shadow DOM to prevent CSS conflicts with your page. Use the theme config to customise it:

window.AgentKit.init({
  agentId: 'your-agent-id',
  tenantId: 'your-tenant-id',
  apiBaseUrl: 'https://api.klicforge.ai',
  theme: {
    mode: 'dark',
    accentColor: '#ec4899',
    fontFamily: "'Inter', sans-serif",
    borderRadius: '8px',
  },
});

Theme values become CSS custom properties inside the shadow root — --ak-accent, --ak-accent-hover, --ak-font, --ak-radius. Setting those variables on your own page has no effect; pass theme instead.


Content Security Policy

If your site sends a CSP, the widget needs:

| Directive | Value | | ------------- | ------------------------------------------------------------ | | script-src | https://unpkg.com (CDN install only) | | connect-src | your apiBaseUrl | | img-src | your apiBaseUrl and any avatar host, plus data: | | style-src | 'unsafe-inline' — styles are injected into the shadow root |

Shadow DOM does not exempt inline styles from CSP, so style-src 'unsafe-inline' is currently required.


Requirements

  • Node.js ≥ 18 (for npm usage)
  • Modern browser with Shadow DOM support