@aikaara/chat-sdk

中文文档
1.5.32发布于 2026/11/05MITTypeScriptESM + CJS近 30 天下载 3309

Aikaara Chat SDK — embeddable chat widget and headless client

原始使用文档
文档目录20 节
  1. Installation
  2. Three Entry Points
  3. Quick Start — Chat Widget (CDN)
  4. Quick Start — Headless Client
  5. Quick Start — Headless with JWT Auth (Dashboard / Internal …
  6. App Context — Connecting the Agent to Your App
  7. Why This Matters
  8. Providing Context on Route Changes
  9. What Happens Under the Hood
  10. AppContext Interface
  11. Handling Navigation
  12. Framework Examples
  13. FormBridge — AI-Driven Form Editing
  14. How It Works
  15. Integration Steps
  16. Lower-Level: Action Events on AikaaraChatClient
  17. React Hook Example
  18. Agent Events
  19. TypeScript Types
  20. License

@aikaara/chat-sdk

Embeddable chat widget and headless client for the Aikaara AI agent platform.

Installation

npm install @aikaara/chat-sdk

Three Entry Points

Import PathUse Case
@aikaara/chat-sdkFull bundle: widget + headless + mount()/unmount()
@aikaara/chat-sdk/headlessHeadless only: no DOM dependency. For React Native, custom UI, server-side
@aikaara/chat-sdk/uiWeb Components only

Quick Start — Chat Widget (CDN)

<script src="https://cdn.aikaara.com/chat-sdk/latest/aikaara-chat.iife.js"></script>
<script>
  AikaaraChat.mount({
    baseUrl: 'https://api.aikaara.com',
    userToken: 'ut_...',
    title: 'Support',
    primaryColor: '#6366f1',
  });
</script>

Quick Start — Headless Client

import { AikaaraChatClient } from '@aikaara/chat-sdk/headless';

const client = new AikaaraChatClient({
  baseUrl: 'https://api.aikaara.com',
  userToken: 'ut_...',
  apiKey: 'ak_...',
});

await client.connect();
client.on('message:received', (msg) => console.log(msg.content));
client.on('stream:update', ({ content }) => updateUI(content));

await client.sendMessage('Hello!');

Quick Start — Headless with JWT Auth (Dashboard / Internal Apps)

import { AikaaraChatClient } from '@aikaara/chat-sdk/headless';

const client = new AikaaraChatClient({
  baseUrl: 'https://api.aikaara.com',
  userToken: 'ut_...',
  authToken: jwtToken, // Bearer token for REST calls
  channel: 'sidekick',
});

await client.connect();

App Context — Connecting the Agent to Your App

The SDK doesn't know about your app's routes, pages, or entities. You provide that context at runtime via setContext(), and the agent uses it to navigate, edit forms, and stay aware of what the user is doing.

Why This Matters

Without context, the agent doesn't know:

  • What page the user is on (/products/42? /orders?)
  • What routes exist in your app (so it can navigate)
  • What entity the user is viewing (so it can edit the right form)

Providing Context on Route Changes

Call setContext() whenever the user navigates in your app:

import { AikaaraChatClient } from '@aikaara/chat-sdk/headless';

const client = new AikaaraChatClient({
  baseUrl: 'https://api.aikaara.com',
  userToken: 'ut_...',
  authToken: jwt,
  channel: 'sidekick',
});

await client.connect();

// Call on every route change (e.g., in a React useEffect, Vue watch, etc.)
client.setContext({
  currentPage: '/products/42',
  entityType: 'product',
  entityId: '42',
  availableRoutes: {
    'Product list': '/products',
    'Order list': '/orders',
    'Customer list': '/customers',
    'Settings': '/settings',
    'Analytics': '/analytics',
  },
  custom: {
    formFields: ['name', 'price', 'description', 'category'],
    userName: 'Jane',
  },
});

What Happens Under the Hood

  1. setContext() sends a PATCH to the backend, storing the context in conversation metadata
  2. The backend interpolates {{current_page}}, {{available_routes}}, and {{custom_context}} into the agent's system prompt
  3. The agent now knows your app's routes and can call navigate_to with them
  4. The agent knows the current entity and can call edit_current_entity for it

AppContext Interface

interface AppContext {
  /** Current page/route path in your app (e.g., '/products/42') */
  currentPage: string;
  /** Entity type on the current page (e.g., 'product', 'order') */
  entityType?: string;
  /** Entity ID on the current page */
  entityId?: string | number;
  /** Project/workspace ID if applicable */
  projectId?: string | number;
  /** Routes the agent can navigate to — map of label to path */
  availableRoutes?: Record<string, string>;
  /** Any additional context for the agent (form fields, user info, etc.) */
  custom?: Record<string, unknown>;
}

Handling Navigation

The agent doesn't control your router — it returns a path, and you handle it:

client.on('action:navigate', ({ navigate_to }) => {
  // Your router (React Router, Vue Router, Next.js, etc.)
  router.push(navigate_to);
});

The navigate_to value will be one of the paths from your availableRoutes, or a path the agent constructs from context (e.g., /products/42).

Framework Examples

React (with React Router)

import { useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router';

function useSidekickContext(client: AikaaraChatClient) {
  const location = useLocation();
  const navigate = useNavigate();

  // Update context on route change
  useEffect(() => {
    const match = location.pathname.match(/^\/(products|orders|customers)\/(\d+)/);
    client.setContext({
      currentPage: location.pathname,
      entityType: match?.[1],
      entityId: match?.[2],
      availableRoutes: {
        'Products': '/products',
        'Orders': '/orders',
        'Customers': '/customers',
        'Analytics': '/analytics',
      },
    });
  }, [location.pathname]);

  // Handle navigation from agent
  useEffect(() => {
    const off = client.on('action:navigate', ({ navigate_to }) => {
      navigate(navigate_to);
    });
    return off;
  }, [navigate]);
}

Vue 3 (with Vue Router)

import { watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';

function useSidekickContext(client: AikaaraChatClient) {
  const route = useRoute();
  const router = useRouter();

  watch(() => route.fullPath, (path) => {
    client.setContext({
      currentPage: path,
      entityType: route.params.type as string,
      entityId: route.params.id as string,
      availableRoutes: { Products: '/products', Orders: '/orders' },
    });
  }, { immediate: true });

  client.on('action:navigate', ({ navigate_to }) => {
    router.push(navigate_to);
  });
}

Vanilla JS / Any Framework

// On route change (however your app detects it)
window.addEventListener('popstate', () => {
  client.setContext({ currentPage: window.location.pathname });
});

// On navigation action from agent
client.on('action:navigate', ({ navigate_to }) => {
  window.history.pushState(null, '', navigate_to);
  // Trigger your app's route handler
});

FormBridge — AI-Driven Form Editing

The FormBridge lets AI agents visually edit forms in your application. When the agent calls tools like edit_current_entity, the bridge pushes field updates to your registered form — the user sees changes live and can confirm or reject them.

How It Works

  1. Your agent has tools like edit_current_entity, save_current_entity, and test_tool_by_id
  2. When the agent calls these tools, the result flows through the WebSocket as a tool_execution_end event
  3. The SDK's AikaaraChatClient parses the result and emits typed action events
  4. FormBridge listens for these events and pushes updates to your registered form

Integration Steps

1. Import FormBridge from the SDK

import { AikaaraChatClient, FormBridge } from '@aikaara/chat-sdk/headless';

2. Pass it your AikaaraChatClient

const client = new AikaaraChatClient({
  baseUrl: 'https://api.aikaara.com',
  userToken: 'ut_...',
  authToken: jwtToken,
  channel: 'sidekick',
});

const bridge = new FormBridge(client);
await client.connect();

3. Register your forms with registerForm()

Call this when your form component mounts. Only one form can be active at a time (the current page).

bridge.registerForm({
  entityType: 'product',       // matches what the agent sends
  entityId: 42,                // the entity being edited
  onFieldUpdate: (fields) => {
    // Update your form UI — fields is FieldUpdate[]
    for (const { field, value } of fields) {
      formState[field] = value;
      rerenderField(field);    // your UI framework's update mechanism
    }
  },
  onSave: async () => {
    // Called when the agent triggers save_current_entity
    await api.updateProduct(42, formState);
  },
  onTest: async (params) => {
    // Optional: called when the agent triggers test_tool_by_id
    await api.testProduct(42, params);
  },
  getCurrentValues: () => ({ ...formState }),
});

// Unregister when the form unmounts
bridge.unregisterForm('product', 42);

4. The bridge automatically handles tool results

When the agent calls these backend tools, the bridge takes action automatically:

Agent ToolBridge Action
edit_current_entityCalls onFieldUpdate() with the field changes
save_current_entityCalls onSave() on the registered form
test_tool_by_idCalls onTest() with parameters

If no form is registered when an edit_current_entity result arrives, the bridge queues it. When a matching form later registers, the queued edits are applied automatically.

5. Build your own confirmation UI using bridge events

bridge.on('edit:applied', ({ entityType, entityId, fields }) => {
  // Show a confirmation banner: "AI edited: name, description"
  showBanner({
    message: `AI edited: ${fields.map(f => f.field).join(', ')}`,
    onConfirm: () => dismissBanner(),
    onReject: () => {
      // Revert fields using previousValue
      revertFields(fields);
      dismissBanner();
    },
    onSave: async () => {
      await bridge.requestSave();
      dismissBanner();
    },
  });
});

bridge.on('edit:pending', ({ entityType, entityId, fields }) => {
  // Edits queued — no matching form registered yet
  // Navigate the user to the right page, then the form will pick them up
});

bridge.on('save:success', () => toast.success('Saved by AI'));
bridge.on('save:error', ({ error }) => toast.error(`Save failed: ${error}`));
bridge.on('test:triggered', ({ toolId, parameters }) => {
  // Show test UI if needed
});

Lower-Level: Action Events on AikaaraChatClient

If you don't need FormBridge and want to handle actions yourself:

client.on('action:edit_entity', (action) => {
  // action: { entity_type, entity_id, fields: FieldUpdate[] }
});

client.on('action:save_entity', () => {
  // Trigger save
});

client.on('action:navigate', ({ navigate_to }) => {
  // Route change: e.g., router.push(navigate_to)
});

client.on('action:test_tool', ({ tool_id, parameters }) => {
  // Run a tool test
});

// Raw tool lifecycle events
client.on('tool:start', ({ toolName, args }) => showSpinner(toolName));
client.on('tool:end', ({ toolName, result, isError }) => hideSpinner(toolName));

React Hook Example

import { useEffect, useRef } from 'react';
import type { FormBridge, FieldUpdate } from '@aikaara/chat-sdk/headless';

function useFormBridge(
  bridge: FormBridge,
  options: {
    entityType: string;
    entityId: string | number | undefined;
    onFieldUpdate: (fields: FieldUpdate[]) => void;
    onSave: () => Promise<void>;
    onTest?: (params?: Record<string, unknown>) => Promise<void>;
    getCurrentValues: () => Record<string, unknown>;
  }
) {
  const refs = useRef(options);
  refs.current = options;

  useEffect(() => {
    if (!options.entityId) return;
    bridge.registerForm({
      entityType: options.entityType,
      entityId: options.entityId,
      onFieldUpdate: (f) => refs.current.onFieldUpdate(f),
      onSave: () => refs.current.onSave(),
      onTest: refs.current.onTest ? (p) => refs.current.onTest!(p) : undefined,
      getCurrentValues: () => refs.current.getCurrentValues(),
    });
    return () => bridge.unregisterForm(options.entityType, options.entityId!);
  }, [bridge, options.entityType, options.entityId]);
}

Agent Events

Events broadcast over ActionCable from the Aikaara Rails backend:

EventDescription
statusProcessing state (processing/completed)
message_start/update/endStreaming response lifecycle
message_queuedUser message acknowledged
tool_execution_start/update/endTool call lifecycle
agent_start/endAgent session lifecycle
turn_start/endConversation turn lifecycle
auto_retry_start/endAuto-retry on failure
cancelledProcessing cancelled

TypeScript Types

All types are exported from @aikaara/chat-sdk/headless:

import type {
  // Connection
  ConnectionConfig, ConnectionState, ChatClientConfig,
  // Messages
  Message, ToolCall, ToolCallResult,
  // Events
  AgentEvent, AgentEventType, ChatEvents,
  // App Context
  AppContext,
  // Form Bridge
  FieldUpdate, FormRegistration, FormBridgeEvents,
  // Actions
  EditEntityAction, SaveEntityAction, TestToolAction, NavigateAction, AgentAction,
} from '@aikaara/chat-sdk/headless';

License

MIT

版本与发布记录

@aikaara/chat-sdk 共发布 140 个版本,首次发布于 2026/03/05;过去 12 个月发布了 140 个版本。

版本发布日期距今
1.5.32latest2026/11/051 天前
1.5.312026/10/2710 天前
1.5.302026/10/2611 天前
1.5.292026/10/2611 天前
1.5.282026/10/2611 天前
1.5.262026/10/2314 天前

关于 @aikaara/chat-sdk 的常见问题

@aikaara/chat-sdk 是什么?

Aikaara Chat SDK — embeddable chat widget and headless client

如何安装 @aikaara/chat-sdk?

在项目目录运行 npm install @aikaara/chat-sdk,或使用 pnpm add / yarn add / bun add。

@aikaara/chat-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@aikaara/chat-sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@aikaara/chat-sdk 可以商用吗?

@aikaara/chat-sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@aikaara/chat-sdk 的下载量有多少?

根据 npm 官方下载统计,@aikaara/chat-sdk 在 2026/10/05 – 2026/11/03 这 30 天内被下载 3,309 次,日均约 110 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@aikaara/chat-sdk 还在维护吗?

最新版本 1.5.32 发布于 2026/11/05(1 天前);至今共发布 140 个版本,其中过去 12 个月发布了 140 个;首次发布于 2026/03/05。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。