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

@sunnyhealthai/agents-sdk

v0.0.65

Published

Embed the Sunny Agents chat experience inside any app.

Readme

@sunnyhealthai/agents-sdk

Embed the Sunny Agents chat experience into any web application. This SDK provides both a headless client for custom UI implementations and a ready-to-use vanilla JavaScript chat widget.

Installation

npm install @sunnyhealthai/agents-sdk
# or
pnpm add @sunnyhealthai/agents-sdk
# or
yarn add @sunnyhealthai/agents-sdk

Quick Start

Simplified API (Recommended)

The easiest way to get started is with createSunnyChat(), which automatically handles authentication and initialization. You need three required parameters: partnerIdentifier, publicKey, and authType. All auth configuration details (token exchange config, audience, etc.) are fetched from the server automatically.

Path of least resistance: Use createSunnyChat as your entry point and pick an auth type by simplicity:

  1. passwordless — No external auth needed. User starts anonymous and verifies via email/SMS when prompted in the chat.
  2. tokenExchange — You already have Auth0, Firebase, or similar. Provide idTokenProvider only; the server supplies audience, clientId, organization, etc.

Passwordless

Starts as anonymous chat. Users can verify via email/SMS when prompted. Customize with headerTitle, placeholder, colors, and other appearance options:

import { createSunnyChat } from "@sunnyhealthai/agents-sdk";

const chat = createSunnyChat({
  container: document.getElementById("chat"),
  partnerIdentifier: "your-partner-name",
  publicKey: "pk-sunnyagents_abc_xyz",
  authType: "passwordless",
  headerTitle: "Sunny Agents",
  placeholder: "Ask anything…",
  colors: { primary: "#006fff", secondary: "#212124", accent: "#22c55e" },
});

// Clean up when unmounting
// chat.destroy();

Custom Token Exchange

For partners with existing auth (Auth0, Firebase, etc.). You provide idTokenProvider only; the server supplies audience, clientId, organization, and tokenExchangeUrl based on your publicKey:

const chat = createSunnyChat({
  container: document.getElementById("chat"),
  partnerIdentifier: "your-partner-name",
  publicKey: "pk-sunnyagents_abc_xyz",
  authType: "tokenExchange",
  idTokenProvider: async () => localStorage.getItem("id_token"),
});

Note: idTokenProvider is required when authType is 'tokenExchange'. All token exchange configuration comes from the server — you do not pass tokenExchange when using createSunnyChat.

Profile sync during auth upgrade

When upgrading from anonymous to authenticated, you can optionally send user profile, address, and insurance data so the backend persists it atomically during auth:

import { createSunnyChat, type AuthUpgradeProfile, type AuthUpgradeAddress, type AuthUpgradeInsurance } from "@sunnyhealthai/agents-sdk";

const chat = createSunnyChat({
  container: document.getElementById("chat"),
  partnerIdentifier: "your-partner-name",
  publicKey: "pk-sunnyagents_abc_xyz",
  authType: "tokenExchange",
  idTokenProvider: async () => getMyUserToken(),
  authUpgradeProfileSync: {
    user_profile: {
      first_name: "John",
      last_name: "Doe",
      phone: "+1234567890",
      date_of_birth: "1990-01-01", // YYYY-MM-DD
      gender: "male",
    },
    user_address: {
      address_line_1: "123 Main St",
      city: "San Francisco",
      state: "CA",
      zip_code: "94102",
      country: "USA",
    },
    insurances: [{
      partner_plan_id: "uuid-from-partner-plans",
      member_id: "M123456",
      group_id: "G789",
    }],
  },
});

authUpgradeProfileSync can also be an async provider: async () => ({ user_profile: {...} }).

Referencing plans by alias

Each insurance entry must reference a plan with exactly one of partner_plan_id (the plan's UUID) or plan_alias (a developer-defined alias configured for your partner). Aliases let you use a stable, human-readable identifier instead of managing raw UUIDs, and are resolved at runtime, scoped to your connection's partner:

insurances: [{
  plan_alias: "gold-ppo-2024", // instead of partner_plan_id
  member_id: "M123456",
  group_id: "G789",
}],

Alias resolution requires partnerIdentifier on the connection. Contact your Sunny integration owner (or use the partner dashboard) to view loaded plans and manage aliases.

Switching Auth Type at Runtime

The returned instance includes setAuthType() for switching authentication at runtime:

const chat = createSunnyChat({
  container: document.getElementById("chat"),
  partnerIdentifier: "your-partner-name",
  publicKey: "pk-sunnyagents_abc_xyz",
  authType: "passwordless",
});

// Later, switch to token exchange with a provider
await chat.setAuthType("tokenExchange", {
  idTokenProvider: async () => getMyUserToken(),
});

Brand Customization

Enterprise partners can customize the SDK appearance to match their brand via configuration—no source changes required:

const chat = createSunnyChat({
  container: document.getElementById("chat"),
  partnerIdentifier: "your-partner-name",
  publicKey: "pk-sunnyagents_abc_xyz",
  authType: "passwordless",
  // Appearance
  headerTitle: "Support Chat",
  placeholder: "How can we help?",
  fontSize: "16px", // Base font size (e.g. "14px", "1rem")
  fontFamily: "'Inter', sans-serif", // Custom font
  colors: {
    primary: "#006fff",
    secondary: "#212124",
    accent: "#22c55e",
    background: "#fff", // Modal and content background
    text: "#212124", // Main text color
  },
  dimensions: {
    width: "1200px",
    height: "800px",
    triggerMaxWidth: "500px",
  },
});

Drop-in Chat Widget (Advanced)

For advanced use when you need to bypass server-driven config and provide tokenExchange or idTokenProvider manually, use attachSunnyChat() with a SunnyAgentsConfig:

<div id="sunny-chat" style="height: 520px;"></div>
<script type="module">
  import { attachSunnyChat } from "@sunnyhealthai/agents-sdk";

  const { client, destroy } = attachSunnyChat({
    container: document.getElementById("sunny-chat"),
    config: {
      websocketUrl: "wss://chat.api.sunnyhealthai-staging.com",
      idTokenProvider: async () => localStorage.getItem("id_token"),
      tokenExchange: {
        partnerName: "your-partner-name",
        audience: "https://api.sunnyhealthai-staging.com",
        clientId: "your-client-id",
        organization: "your-organization-id",
      },
    },
    headerTitle: "Sunny Agents",
    placeholder: "Ask anything…",
  });

  // Clean up when unmounting
  // destroy();
</script>

Headless Client

For full custom UIs when you do not want the built-in chat widget, use the headless client:

import { SunnyAgentsClient } from "@sunnyhealthai/agents-sdk";

const client = new SunnyAgentsClient({
  websocketUrl: "wss://chat.api.sunnyhealthai-staging.com",
  idTokenProvider: async () => localStorage.getItem("id_token"),
  tokenExchange: {
    partnerName: "your-partner-name",
    audience: "https://api.sunnyhealthai-staging.com",
    clientId: "your-client-id",
    organization: "your-organization-id",
  },
});

// Listen to events
client.on("snapshot", (snapshot) => {
  console.log("Current state:", snapshot);
});

client.on("streamingDelta", ({ conversationId, messageId, text }) => {
  console.log("Streaming text:", text);
});

client.on("streamingDone", ({ conversationId, messageId, text }) => {
  console.log("Message complete:", text);
});

// Send a message
await client.sendMessage("Hello, Sunny!");

Configuration

Authenticated Mode

When using authenticated mode, provide an idTokenProvider function that returns an ID token, along with tokenExchange configuration. The SDK will automatically exchange the ID token for an access token:

const client = new SunnyAgentsClient({
  websocketUrl: "wss://chat.api.sunnyhealthai-staging.com",
  idTokenProvider: async () => {
    // Return your ID token (e.g., from Auth0, Firebase, etc.)
    return localStorage.getItem("id_token");
  },
  tokenExchange: {
    partnerName: "your-partner-name", // e.g., "sunny-health-external-mock"
    audience: "https://api.sunnyhealthai-staging.com",
    clientId: "your-auth0-client-id",
    organization: "your-organization-id",
    tokenExchangeUrl: "https://auth.sunnyhealth.live/oauth/token", // Optional, defaults to this
  },
});

Token Exchange Flow:

  1. Your app provides an ID token via idTokenProvider
  2. The SDK exchanges it for an access token using the configured token exchange endpoint
  3. The access token is cached and automatically refreshed when expired
  4. The access token is used to authenticate WebSocket connections and API requests

Configuration Options:

  • idTokenProvider: Function that returns a Promise resolving to an ID token string or null
  • tokenExchange.partnerName: Partner identifier used to construct the subject token type (e.g., "sunny-health-external-mock")
  • tokenExchange.audience: API audience for the access token (e.g., "https://api.sunnyhealthai-staging.com")
  • tokenExchange.clientId: Auth0 client ID for token exchange
  • tokenExchange.organization: Organization ID (required for token exchange)
  • tokenExchange.tokenExchangeUrl: Optional token exchange endpoint URL (defaults to "https://auth.sunnyhealth.live/oauth/token")

For anonymous mode (when tokenExchange is not used), you can pass partnerName at the top level of the config to identify the partner.

Anonymous Mode

The recommended path is createSunnyChat with authType: 'passwordless' (see Quick Start). The user starts unauthenticated and can verify via email/SMS when prompted.

With SunnyAgentsClient (Low-Level)

For anonymous/local-only conversations without the built-in widget, omit idTokenProvider and tokenExchange. The SDK will operate in anonymous mode:

const client = new SunnyAgentsClient({
  websocketUrl: "wss://chat.api.sunnyhealthai-staging.com",
  partnerName: "your-partner-name",  // Optional: partner identifier for websocket
  // No idTokenProvider or tokenExchange = anonymous mode
});

Or explicitly disable server conversation creation:

const client = new SunnyAgentsClient({
  websocketUrl: "wss://chat.api.sunnyhealthai-staging.com",
  partnerName: "your-partner-name",  // Optional
  createServerConversations: false, // Explicitly disable server persistence
});

With the vanilla widget, you can use the anonymous option:

attachSunnyChat({
  container: document.getElementById("sunny-chat"),
  config: {
    websocketUrl: "wss://chat.api.sunnyhealthai-staging.com",
    partnerName: "your-partner-name",  // Optional: partner identifier for websocket
  },
  anonymous: true, // Enables anonymous mode (same as omitting idTokenProvider)
});

Note: Prefer createSunnyChat with authType: 'passwordless' when you want anonymous chat with optional in-chat verification. The low-level options above are for custom UIs or when bypassing server-driven config.

Note: partnerName can be passed in anonymous mode to identify the partner to the backend. It is sent as the partner_identifier query parameter on the websocket connection. createServerConversations defaults to true if both idTokenProvider and tokenExchange are provided, otherwise false.

Passwordless Authentication

The path of least resistance for passwordless is createSunnyChat with authType: 'passwordless' (see Quick Start). Users start anonymous and verify via email/SMS when prompted.

Benefits:

  • No page redirects - all authentication happens via WebSocket
  • Email or SMS OTP verification
  • Seamless integration with chat widget
  • Optional chat history migration on authentication
  • Works with token exchange for Sunny access tokens

With createSunnyChat (Recommended):

import { createSunnyChat } from '@sunnyhealthai/agents-sdk';

// Passwordless is handled automatically - verification UI appears in chat
const chat = createSunnyChat({
  container: document.getElementById('chat'),
  partnerIdentifier: 'your-partner-name',
  publicKey: 'pk-sunnyagents_abc_xyz',
  authType: 'passwordless',
});

When using createSunnyChat with authType: 'passwordless', a PasswordlessAuthManager is created automatically and verification flow tags in chat messages ({verification_flow}) render a passwordless login form within the chat interface.

Advanced: Use PasswordlessAuthManager directly:

For custom flows, the PasswordlessAuthManager is exported as an internal API:

import { PasswordlessAuthManager, LLMWebSocketManager, attachSunnyChat } from '@sunnyhealthai/agents-sdk';

// Create shared WebSocket manager for passwordless auth and chat
const wsManager = new LLMWebSocketManager({
  websocketUrl: 'wss://chat.api.sunnyhealthai-staging.com',
});

// Initialize passwordless auth manager
const passwordlessAuth = new PasswordlessAuthManager({
  wsManager, // Required - WebSocket manager instance
  migrateHistory: true, // Migrate anonymous chat history to authenticated user
  storageType: 'sessionStorage', // 'memory', 'sessionStorage', or 'localStorage'
});

// Start passwordless login flow
await passwordlessAuth.startLogin({ email: '[email protected]' });
// or
await passwordlessAuth.startLogin({ phoneNumber: '+1234567890' });

// Verify OTP code
await passwordlessAuth.verifyCode({
  email: '[email protected]', // or phoneNumber
  code: '123456',
});

// Check authentication status
if (passwordlessAuth.isAuthenticated()) {
  const userId = passwordlessAuth.getUserId();
  const email = passwordlessAuth.getEmail();
}

// Use with chat widget
const { client, destroy } = attachSunnyChat({
  container: document.getElementById('chat'),
  passwordlessAuth, // Enables verification flow UI in chat messages
  config: {
    websocketUrl: 'wss://chat.api.sunnyhealthai-staging.com',
    wsManager, // Share the same WebSocket manager
  },
});

Configuration Options (PasswordlessAuthConfig):

  • wsManager: LLMWebSocketManager - Required - WebSocket manager instance for sending passwordless auth messages
  • migrateHistory?: boolean - Whether to migrate anonymous chat history to authenticated user on successful auth (default: false)
  • tokenExchange?: TokenExchangeConfig - Optional token exchange configuration to obtain Sunny access tokens
  • storageKey?: string - Optional storage key for persisting auth state (default: in-memory only)
  • storageType?: 'memory' | 'sessionStorage' | 'localStorage' - Storage type for auth state (default: 'memory')

Methods:

  • startLogin(options: PasswordlessStartOptions): Start passwordless login flow by sending OTP code

    • options.email?: string - Email address (provide either email or phoneNumber)
    • options.phoneNumber?: string - Phone number (provide either email or phoneNumber)
    • Returns Promise<void>
  • verifyCode(options: PasswordlessVerifyOptions): Verify OTP code and authenticate user

    • options.email?: string - Email address used for login
    • options.phoneNumber?: string - Phone number used for login
    • options.code: string - OTP verification code
    • Returns Promise<void>
  • getIdToken(): string | null: Get stored user ID (for compatibility with token exchange flows)

  • getUserId(): string | null: Get authenticated user ID, or null if not authenticated

  • getEmail(): string | null: Get authenticated user's email, or null if not authenticated

  • getAccessToken(): Promise<string | null>: Get Sunny access token via token exchange if configured

  • isAuthenticated(): boolean: Check if user is authenticated

  • logout(): void: Clear authentication state

  • onAuthStateChange(callback: (isAuthenticated: boolean) => void): Subscribe to authentication state changes. Returns unsubscribe function.

  • onOtpSent(callback: (connection: 'email' | 'sms') => void): Subscribe to OTP sent events. Returns unsubscribe function.

  • destroy(): void: Clean up resources and event listeners

See the vanilla chat example for a complete implementation with passwordless authentication.

Breaking change: auth upgrade API

LLMWebSocketManager.upgradeAuth and upgradeAuthIfPossible now use options-based signatures:

Before:

await wsManager.upgradeAuth(token, true);
await wsManager.upgradeAuthIfPossible(true);

After:

await wsManager.upgradeAuth({ token, migrateHistory: true });
await wsManager.upgradeAuthIfPossible({ migrateHistory: true });

Profile-sync data can be included in both:

await wsManager.upgradeAuth({
  token,
  migrateHistory: true,
  user_profile: { first_name: "John", last_name: "Doe" },
  user_address: { address_line_1: "123 Main St", city: "SF", state: "CA", zip_code: "94102" },
});

await wsManager.upgradeAuthIfPossible({
  migrateHistory: true,
  profileSync: { user_profile: { first_name: "John" } },
});

API Reference

createSunnyChat

Recommended API - Unified entry point that automatically handles authentication and initialization. Auth configuration is retrieved from the server based on your publicKey.

createSunnyChat(options: UnifiedSunnyChatOptions): VanillaChatInstance

Configuration Options (UnifiedSunnyChatOptions):

  • container: HTMLElement - Container element to mount the chat widget
  • partnerIdentifier: string - Required - Partner identifier (sent as partner_identifier query param on websocket)
  • publicKey: string - Required - Public API key (e.g., "pk-sunnyagents_abc_xyz")
  • authType: SdkAuthType - Required - Authentication type: 'passwordless' or 'tokenExchange'
  • idTokenProvider?: () => Promise<string | null> - Function that returns an ID token. Required when authType is 'tokenExchange'
  • websocketUrl?: string - Override WebSocket URL (default: "wss://chat.api.sunnyhealthai-staging.com")
  • wsManager?: LLMWebSocketManager - Share a WebSocket manager across instances
  • devRoute?: string - Developer route/destination for token exchange. If not provided, auto-extracted from URL query parameters (?dev-route=...)
  • headerTitle?: string - Title displayed in chat header (default: "Sunny Agents")
  • placeholder?: string - Input placeholder text (default: "Ask anything...")
  • colors?: VanillaChatColors - Custom theme colors
  • fontSize?: string - Base font size for chat content (e.g. "14px", "1rem"). Default: "14px"
  • fontFamily?: string - Font family for the chat UI (e.g. "'Inter', sans-serif"). Default: Lato
  • dimensions?: VanillaChatDimensions - Widget dimensions (modal width/height, trigger max-width)
  • authUpgradeProfileSync?: AuthUpgradeProfileSyncData | (() => Promise<AuthUpgradeProfileSyncData | null>) - Optional profile-sync data (user_profile, user_address, insurances) sent with auth.upgrade

Returns:

  • VanillaChatInstance - Chat instance with:
    • client: SunnyAgentsClient - The underlying client instance
    • destroy(): void - Cleanup function to unmount the widget
    • setAuthType(authType: SdkAuthType, options?: { idTokenProvider?; authUpgradeProfileSync? }): Promise<void> - Switch authentication type at runtime

How It Works:

  1. Creates and mounts the chat widget immediately (synchronous return)
  2. Fetches server-provided auth configuration (SdkAuthConfig) via HTTP in the background
  3. Activates the specified authType using the server configuration
  4. For tokenExchange: proactively connects WebSocket, establishes SDK session, and authenticates — so the first message sends instantly

Important Notes:

  • Auth configuration (audience, clientId, organization, etc.) comes from the server — you do not provide them
  • authType: 'passwordless' starts anonymous with in-chat verification UI
  • authType: 'tokenExchange' — you provide idTokenProvider only; server provides audience, clientId, organization, tokenExchangeUrl (based on publicKey)
  • Use setAuthType() on the returned instance to switch auth types at runtime

SunnyAgentsClient

The headless client for building custom chat UIs.

Constructor Options

new SunnyAgentsClient(config?: SunnyAgentsConfig)

Configuration Options (SunnyAgentsConfig):

  • websocketUrl?: string - WebSocket URL for chat connection (defaults to "wss://chat.api.sunnyhealthai-staging.com")
  • wsManager?: LLMWebSocketManager - Optional WebSocket manager instance to share across multiple clients. Allows sharing the same WebSocket connection between PasswordlessAuthManager and SunnyAgentsClient for seamless authentication. If not provided, a new LLMWebSocketManager will be created.
  • idTokenProvider?: () => Promise<string | null> - Function that returns an ID token for token exchange
  • partnerName?: string - Partner identifier for websocket connection (used when tokenExchange not present, e.g. anonymous mode). Passed as partner_identifier query param.
  • publicKey?: string - Public API key for SDK session creation (e.g., "pk-sunnyagents_abc_xyz")
  • tokenExchange?: TokenExchangeConfig - Token exchange configuration (required if using idTokenProvider)
    • partnerName: string - Partner identifier (e.g., "sunny-health-external-mock")
    • audience: string - API audience for access token (e.g., "https://api.sunnyhealthai-staging.com")
    • clientId: string - Auth0 client ID for token exchange
    • organization: string - Organization ID (required for token exchange)
    • tokenExchangeUrl?: string - Token exchange endpoint (defaults to "https://auth.sunnyhealth.live/oauth/token")
    • devRoute?: string - Developer route/destination
  • sessionStorageKey?: string - Key for session persistence (defaults to "sunny_agents_session_id")
  • initialConversationId?: string - Initial conversation ID to use
  • createServerConversations?: boolean - Whether to create/persist conversations on server (defaults to true if idTokenProvider and tokenExchange are provided, otherwise false)
  • authUpgradeProfileSync?: AuthUpgradeProfileSyncData | (() => Promise<AuthUpgradeProfileSyncData | null>) - Optional profile-sync data sent with auth.upgrade

Methods

  • sendMessage(text: string, options?: SendMessageOptions): Send a message and stream the response

    • options.conversationId: Target conversation (defaults to active conversation)
    • options.title: Set conversation title
    • options.files: Array of file attachments (base64 encoded)
    • options.onMessageCreated: Callback when message is created
  • createConversation(title?: string | null, conversationId?: string | null): Create a new conversation. Returns a Promise resolving to the conversation ID.

  • setActiveConversation(conversationId: string | null): Set the active conversation ID.

  • getSnapshot(): Get current state snapshot. Returns SunnyAgentsClientSnapshot with conversations array and activeConversationId.

WebSocket session vs conversation

The server may assign a new WebSocket session after a disconnect (the SDK does not resume the previous session_id). Conversation identity is separate: conversation_id, the in-memory conversations map, and activeConversationId are kept for the lifetime of the SunnyAgentsClient instance (same tab). The next sendMessage continues the same conversation on the new transport session. Interrupted assistant streams are finalized (spinner cleared, partial text kept) when the socket closes.

  • sendMcpApproval(conversationId: string, approvalRequestId: string, approve: boolean, reason?: string | null): Send an MCP approval response for a pending approval request.

  • setIdTokenProvider(provider: (() => Promise<string | null>) | undefined): Dynamically update the ID token provider. Useful for updating authentication after a user logs in. Pass undefined to clear the provider.

  • on(event, handler): Subscribe to events. Returns an unsubscribe function.

  • off(event, handler): Unsubscribe from events

  • subscribe(fn): Subscribe to all state changes with a single callback. Returns an unsubscribe function.

Events

  • snapshot: Emitted when state changes
  • conversationCreated: Emitted when a new conversation is created
  • messagesUpdated: Emitted when messages are updated
  • streamingDelta: Emitted during message streaming (contains conversationId, messageId, text)
  • streamingDone: Emitted when streaming completes
  • quickResponses: Emitted when quick response suggestions are available

attachSunnyChat

Mount a ready-to-use chat widget.

Options

  • container: HTMLElement - HTMLElement to mount the chat widget
  • client?: SunnyAgentsClient - Optional pre-configured client instance. If not provided, a new client will be created from config.
  • config?: SunnyAgentsConfig - Configuration for creating a new client (same as SunnyAgentsClient constructor options). Ignored if client is provided.
  • headerTitle?: string - Title displayed in the chat header (default: "Sunny Agents")
  • placeholder?: string - Input placeholder text (default: "Ask anything…")
  • anonymous?: boolean - Enable anonymous mode. Sets createServerConversations: false when no token provider is configured (default: false)
  • conversationId?: string - Optional conversation ID to use for anonymous sessions. If not provided, a new UUID will be generated (in-memory only, no persistence).
  • passwordlessAuth?: PasswordlessAuthManager - Optional PasswordlessAuthManager instance for handling verification flow in chat messages. When provided, verification flow tags in messages ({verification_flow}) will render a passwordless login form.
  • colors?: VanillaChatColors - Custom theme colors
    • primary?: string - Primary color for user messages, send button, and focus states (default: "#006fff")
    • secondary?: string - Secondary color for text and UI elements (default: "#212124")
    • accent?: string - Accent color for success states and highlights (default: "#22c55e")
    • background?: string - Background color for modal and content areas (default: "#fff")
    • text?: string - Main text color (default: "#212124")
  • fontSize?: string - Base font size for chat content (e.g. "14px", "1rem"). Default: "14px"
  • fontFamily?: string - Font family for the chat UI (e.g. "'Inter', sans-serif"). Default: Lato
  • dimensions?: VanillaChatDimensions - Widget dimensions
    • width?: string - Modal width when expanded (default: "1390px")
    • height?: string - Modal height when expanded (default: "980px")
    • triggerMaxWidth?: string - Max width of collapsed trigger bar (default: "600px")

Returns (VanillaChatInstance)

  • client: SunnyAgentsClient - The underlying SunnyAgentsClient instance
  • destroy(): void - Cleanup function to unmount the widget and clean up event listeners
  • setAuthType?(authType: SdkAuthType, options?): Promise<void> - Switch auth type at runtime. Only available when the instance was created via createSunnyChat().

File Uploads

Send files as base64-encoded attachments:

await client.sendMessage("Analyze this image", {
  files: [
    {
      filename: "image.png",
      content: "base64-encoded-content-here",
    },
  ],
});

Artifacts

Artifacts (like doctor profiles) are delivered inline in message text via the WebSocket. The backend expands artifact tags just-in-time (JIT) during streaming. Parse message.text for embedded JSON objects with item_type and item_content. The vanilla widget automatically parses and renders them. See the documentation for parsing examples.

Framework Integration

This SDK has no framework dependencies. You can use it with:

  • React: Use the headless client and build your own UI components
  • Vue: Use the headless client with Vue's reactivity system
  • Vanilla JavaScript: Use either the headless client or the attachSunnyChat widget
  • Any other framework: The headless client works with any framework

TypeScript Support

This package includes full TypeScript definitions. Import types and classes as needed:

import {
  createSunnyChat, // Recommended unified API
  SunnyAgentsClient, // Class, not a type
  attachSunnyChat,
  type ConversationState,
  type SunnyAgentMessage,
  type SunnyAgentMessageRole, // 'user' | 'assistant' | 'system'
  type SunnyAgentMessageItem, // Output items (MCP approvals, content fragments, etc.)
  type SunnyAgentMessageContentFragment, // Content fragment within a message item
  type SunnyAgentsConfig,
  type UnifiedSunnyChatOptions, // Unified config type for createSunnyChat
  type SdkAuthType, // 'passwordless' | 'tokenExchange'
  type SdkAuthConfig, // Server-provided auth configuration
  type AuthUpgradeProfile, // Profile fields for auth.upgrade
  type AuthUpgradeAddress, // Address for auth.upgrade
  type AuthUpgradeInsurance, // Insurance for auth.upgrade
  type AuthUpgradeProfileSyncData, // Container for profile-sync data
  type AuthUpgradeRequest, // Options for upgradeAuth
  type SendMessageOptions,
  type SunnyAgentsClientSnapshot,
  type FileAttachment, // File attachment for sendMessage
  type ChatArtifact,
  type DoctorProfileArtifact,
  type ProviderSearchResultsArtifact, // Provider search results artifact
  type ProviderResult, // Individual provider result
  type LocationResult, // Provider location result
  type VanillaChatOptions,
  type VanillaChatInstance,
  type VanillaChatColors,
  type PasswordlessStartOptions, // Options for starting passwordless login
  type PasswordlessVerifyOptions, // Options for verifying OTP code
  type PasswordlessAuthState, // Current passwordless auth state
} from "@sunnyhealthai/agents-sdk";

// Internal APIs (exported for advanced use cases but not recommended for most users)
import {
  PasswordlessAuthManager, // Internal - Passwordless authentication manager
  LLMWebSocketManager, // Internal - WebSocket manager
  TokenExchangeManager, // Internal - Token exchange manager
  exchangeIdTokenForAccessToken, // Internal - Direct token exchange function
  type PasswordlessAuthConfig, // Internal - Passwordless auth configuration
  type LLMWebSocketConfig, // Internal - WebSocket configuration
  type TokenExchangeConfig, // Internal - Token exchange configuration
  type TokenExchangeResponse, // Internal - Token exchange response
} from "@sunnyhealthai/agents-sdk";