@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-sdkQuick 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:
- passwordless — No external auth needed. User starts anonymous and verifies via email/SMS when prompted in the chat.
- tokenExchange — You already have Auth0, Firebase, or similar. Provide
idTokenProvideronly; 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:
- Your app provides an ID token via
idTokenProvider - The SDK exchanges it for an access token using the configured token exchange endpoint
- The access token is cached and automatically refreshed when expired
- 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 nulltokenExchange.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 exchangetokenExchange.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 messagesmigrateHistory?: 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 tokensstorageKey?: 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 codeoptions.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 useroptions.email?: string- Email address used for loginoptions.phoneNumber?: string- Phone number used for loginoptions.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 authenticatedgetEmail(): string | null: Get authenticated user's email, or null if not authenticatedgetAccessToken(): Promise<string | null>: Get Sunny access token via token exchange if configuredisAuthenticated(): boolean: Check if user is authenticatedlogout(): void: Clear authentication stateonAuthStateChange(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): VanillaChatInstanceConfiguration Options (UnifiedSunnyChatOptions):
container: HTMLElement- Container element to mount the chat widgetpartnerIdentifier: string- Required - Partner identifier (sent aspartner_identifierquery 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 whenauthTypeis'tokenExchange'websocketUrl?: string- Override WebSocket URL (default:"wss://chat.api.sunnyhealthai-staging.com")wsManager?: LLMWebSocketManager- Share a WebSocket manager across instancesdevRoute?: 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 colorsfontSize?: 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: Latodimensions?: 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 instancedestroy(): void- Cleanup function to unmount the widgetsetAuthType(authType: SdkAuthType, options?: { idTokenProvider?; authUpgradeProfileSync? }): Promise<void>- Switch authentication type at runtime
How It Works:
- Creates and mounts the chat widget immediately (synchronous return)
- Fetches server-provided auth configuration (
SdkAuthConfig) via HTTP in the background - Activates the specified
authTypeusing the server configuration - 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 UIauthType: 'tokenExchange'— you provideidTokenProvideronly; server provides audience, clientId, organization, tokenExchangeUrl (based onpublicKey)- 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 betweenPasswordlessAuthManagerandSunnyAgentsClientfor seamless authentication. If not provided, a newLLMWebSocketManagerwill be created.idTokenProvider?: () => Promise<string | null>- Function that returns an ID token for token exchangepartnerName?: string- Partner identifier for websocket connection (used whentokenExchangenot present, e.g. anonymous mode). Passed aspartner_identifierquery param.publicKey?: string- Public API key for SDK session creation (e.g.,"pk-sunnyagents_abc_xyz")tokenExchange?: TokenExchangeConfig- Token exchange configuration (required if usingidTokenProvider)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 exchangeorganization: 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 usecreateServerConversations?: boolean- Whether to create/persist conversations on server (defaults totrueifidTokenProviderandtokenExchangeare provided, otherwisefalse)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 responseoptions.conversationId: Target conversation (defaults to active conversation)options.title: Set conversation titleoptions.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. ReturnsSunnyAgentsClientSnapshotwithconversationsarray andactiveConversationId.
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. Passundefinedto clear the provider.on(event, handler): Subscribe to events. Returns an unsubscribe function.off(event, handler): Unsubscribe from eventssubscribe(fn): Subscribe to all state changes with a single callback. Returns an unsubscribe function.
Events
snapshot: Emitted when state changesconversationCreated: Emitted when a new conversation is createdmessagesUpdated: Emitted when messages are updatedstreamingDelta: Emitted during message streaming (containsconversationId,messageId,text)streamingDone: Emitted when streaming completesquickResponses: Emitted when quick response suggestions are available
attachSunnyChat
Mount a ready-to-use chat widget.
Options
container: HTMLElement- HTMLElement to mount the chat widgetclient?: SunnyAgentsClient- Optional pre-configured client instance. If not provided, a new client will be created fromconfig.config?: SunnyAgentsConfig- Configuration for creating a new client (same asSunnyAgentsClientconstructor options). Ignored ifclientis 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. SetscreateServerConversations: falsewhen 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 colorsprimary?: 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: Latodimensions?: VanillaChatDimensions- Widget dimensionswidth?: 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 underlyingSunnyAgentsClientinstancedestroy(): void- Cleanup function to unmount the widget and clean up event listenerssetAuthType?(authType: SdkAuthType, options?): Promise<void>- Switch auth type at runtime. Only available when the instance was created viacreateSunnyChat().
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
attachSunnyChatwidget - 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";