@sinm/kai-embed-sdk
v0.2.1
Published
Host-page SDK for KAi embed mode. Zero-dependency, framework-agnostic.
Readme
KAi Embed SDK
Host-page SDK for KAi embed mode. Zero-dependency, framework-agnostic.
Quick Start
Floating mode (default)
SDK creates a floating button at bottom-right and an overlay drawer.
import { createKaiEmbedHost } from '@sinm/kai-embed-sdk';
const kai = createKaiEmbedHost({
// Optional: pre-fill the address bar
src: 'https://your-kai-server/?embed=true',
host: {
name: 'My App',
instructions: 'Read the page before making changes.',
},
});If src is omitted, the user inputs the KAi server address in the address bar shown in the drawer. The address is persisted to localStorage by default.
Custom trigger
Use your own button to toggle the drawer:
const kai = createKaiEmbedHost({
trigger: document.querySelector('#my-kai-button'),
src: 'https://your-kai-server/?embed=true',
});Inline mode (embed in your layout)
Mount the drawer inside a container element — no floating overlay:
const kai = createKaiEmbedHost({
container: document.querySelector('#kai-slot'),
src: 'https://your-kai-server/?embed=true',
});Register Custom Commands
kai.registerCommand({
command: 'read_issue',
description: 'Read current issue details',
whenToUse: 'Use when the user asks about the current issue',
sideEffects: 'reads_page_state',
parameters: {
type: 'object',
properties: {
issueId: { type: 'string' },
},
},
handler: async ({ issueId }) => {
const res = await fetch(`/api/issues/${issueId}`);
return res.json();
},
});Built-in Page Commands
Only page.snapshot is enabled by default. Mutating/dangerous commands are locked and must be explicitly enabled:
// Enable specific commands
createKaiEmbedHost({ container, pageCommands: { 'page.fill': true, 'page.click': true } });
// Disable all built-in commands (including snapshot)
createKaiEmbedHost({ container, pageCommands: false });| Command | Implementation | Default |
|---------|---------------|---------|
| page.snapshot | querySelector(selector ?? 'body').innerHTML | enabled |
| page.click | HTMLElement.click() | locked |
| page.fill | Set form field value + dispatch input/change | locked |
| page.evaluate | Evaluate JS expression in host page | locked |
| page.apiRequest | fetch() from host page context (carries cookies) | locked |
Locked commands are advertised in capabilities with status: 'locked' and return permission_denied if called.
Custom Fetch (Dynamic Auth)
page.apiRequest uses the global fetch by default, which sends requests with the host page's cookies — allowing the agent to act as the authenticated user.
For dynamic token injection or custom request handling, pass a fetchFn:
const kai = createKaiEmbedHost({
container,
pageCommands: { 'page.apiRequest': true },
fetchFn: async (input, init) => {
const token = getAuthToken(); // your dynamic token logic
const headers = new Headers(init?.headers);
headers.set('Authorization', `Bearer ${token}`);
return fetch(input, { ...init, headers });
},
});API
createKaiEmbedHost(options) → KaiEmbedHost
| Option | Type | Description |
|--------|------|-------------|
| container | HTMLElement | Mount inline inside this element (no overlay/FAB) |
| trigger | HTMLElement | Custom trigger button (floating mode only, replaces FAB) |
| src | string | Default KAi server URL (user can override in address bar) |
| hostId | string | Stable host identity for multi-host routing |
| host | HostManifest | Name, description, instructions for the agent |
| context | HostContext | Initial page context (url, title, selection, meta) |
| pageCommands | PageCommandOptions | Built-in command config; snapshot enabled, others locked |
| fetchFn | typeof fetch | Custom fetch for page.apiRequest (auth tokens, interceptors) |
| title | string | Drawer header title (default: "KAi") |
| persistAddress | boolean | Persist user-entered address to localStorage (default: true) |
| onError | (error) => void | Error callback for command handler failures |
Methods
| Method | Description |
|--------|-------------|
| registerCommand(options) | Register a custom command; returns unregister function |
| unregisterCommand(name) | Remove a command by name |
| listCommands() | List all registered commands |
| updateContext(ctx) | Push updated page context |
| updateManifest(manifest) | Update host capability guidance |
| announceCapabilities() | Re-send capability declaration |
| open() / close() / toggle() | Control drawer visibility (no-op in inline mode) |
| isOpen() | Check if drawer is open |
| destroy() | Clean up DOM, listeners, and state |
Exported Utilities
| Function | Description |
|----------|-------------|
| isValidKaiUrl(url) | Check if URL is a valid KAi embed URL (has ?embed=true) |
| ensureEmbedParam(url) | Append ?embed=true to URL if missing |
Security
- Origin is derived from the iframe URL — no manual
targetOriginneeded - Messages are validated:
event.source === iframe.contentWindow && event.origin === iframeOrigin - Commands are looked up by name — no
evalof raw input page.snapshotreturns raw DOM HTML; it is not a privacy filterpage.evaluateexecutes arbitrary JS in the host page context — only enable when you trust the KAi agentpage.apiRequestsends fetch with the host page's cookies by default — usefetchFnto customize auth behavior
