# Aria SDK

A production-ready SDK for integrating [Anam AI](https://docs.anam.ai/) into your web application. Simple initialization, automatic session management, and a beautiful AI assistant interface.

[![npm version](https://img.shields.io/npm/v/@centive/aria-sdk.svg)](https://www.npmjs.com/package/@centive/aria-sdk)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Quick Start

```typescript
import { Aria } from '@centive/aria-sdk';

// Initialize with just 3 lines
Aria.init({
  websocketUrl: 'wss://your-server.com/ws',
  userId: 'user_123',
});

// That's it! The floating button appears automatically.
```

## Installation

```bash
npm install @centive/aria-sdk
```

### Peer Dependencies

```bash
npm install react@^18.0.0 react-dom@^18.0.0 @anam-ai/js-sdk@^4.7.0
```

## Features

| Feature | Description |
|---------|-------------|
| **Simple Init** | Single function call to set up everything |
| **Auto Session Management** | WebSocket connection, token refresh, reconnection - all automatic |
| **Dual-Mode UI** | Floating button + center modal for WebSocket-triggered events |
| **Session Persistence** | Sessions survive page navigation and widget close/reopen |
| **Error Recovery** | Automatic reconnection with exponential backoff |
| **Tool Call Indicators** | Visual feedback when AI executes functions |
| **Theme Support** | Light and dark themes |

## Configuration

```typescript
Aria.init({
  // Required
  websocketUrl: string,  // Your backend WebSocket URL
  userId: string,        // Unique user identifier

  // Optional
  theme?: 'light' | 'dark',           // UI theme (default: 'light')
  triggerLabel?: string,              // Button label (default: 'Talk to Aria')
  onError?: (error: Error) => void,   // Error callback
});
```

### Example with All Options

```typescript
import { Aria } from '@centive/aria-sdk';

Aria.init({
  websocketUrl: 'wss://api.example.com/ws',
  userId: 'user_123',
  theme: 'dark',
  triggerLabel: 'Need Help?',
  onError: (error) => {
    console.error('Aria SDK error:', error);
  },
});
```

## API Reference

| Method | Description |
|--------|-------------|
| `Aria.init(config)` | Initialize the SDK |
| `Aria.open(mode?)` | Open assistant widget (`'user'` or `'websocket'`) |
| `Aria.close()` | Close assistant widget |
| `Aria.minimize()` | Minimize to compact state |
| `Aria.maximize()` | Restore from minimized |
| `Aria.destroy()` | Cleanup and remove SDK |
| `Aria.isInitialized()` | Check if SDK is initialized |
| `Aria.isOpen()` | Check if widget is open |
| `Aria.isConnected()` | Check WebSocket connection |
| `Aria.getSessionId()` | Get current session ID |
| `Aria.refreshSession()` | Manually refresh session |

### Programmatic Control

```typescript
// Open the assistant
Aria.open();

// Open in center mode (like incoming call)
Aria.open('websocket');

// Close the assistant
Aria.close();

// Minimize/maximize
Aria.minimize();
Aria.maximize();

// Check status
if (Aria.isConnected()) {
  console.log('Session ID:', Aria.getSessionId());
}

// Cleanup when done
Aria.destroy();
```

## Backend Requirements

The SDK connects to your backend via WebSocket. Your backend handles:

1. **User Authentication** - Validate the connecting user
2. **Session Token Generation** - Create Anam AI sessions
3. **Persona Configuration** - Control AI behavior server-side (avatar, voice, LLM, system prompt)
4. **Message Storage** - Store conversation history (optional)

### Messages from SDK to Backend

#### 1. Session Request (user_trigger)

Sent when user opens the widget to request a new session token.

```json
{
  "user_trigger": true,
  "userId": "user_123",
  "skip_greeting": true  // true after first session (to skip greeting on reopen)
}
```

| Field | Type | Description |
|-------|------|-------------|
| `user_trigger` | boolean | Always `true` when requesting a session |
| `userId` | string | User identifier passed to `Aria.init()` |
| `skip_greeting` | boolean | `true` if user has already had a session (skip greeting on subsequent opens) |

#### 2. Message History

Sent when conversation messages are updated.

```json
{
  "type": "message_history",
  "session_id": "anam_session_abc123",
  "user_id": "user_123",
  "messages": [
    { "role": "persona", "content": "Hello! How can I help?", "timestamp": "2026-01-29T12:00:00.000Z" },
    { "role": "user", "content": "I need help with my account", "timestamp": "2026-01-29T12:00:05.000Z" }
  ]
}
```

#### 3. Message Stream

Sent for real-time message streaming (as user/persona speaks).

```json
{
  "type": "message_stream",
  "session_id": "anam_session_abc123",
  "user_id": "user_123",
  "message": {
    "role": "user",
    "content": "partial transcript...",
    "timestamp": "2026-01-29T12:00:05.000Z",
    "is_final": false
  }
}
```

#### 4. Session End

Sent when user closes the widget (hangs up).

```json
{
  "type": "session_end",
  "session_id": "anam_session_abc123",
  "user_id": "user_123"
}
```

### Expected Response Format

#### Session Token Response

```json
{
  "status": "success",
  "message": "Session created successfully",
  "session_data": {
    "session_id": "sess_abc123",
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "expires_at": "2026-01-27T12:00:00Z"
  },
  "time_taken": 1234
}
```

#### Message Acknowledgments (Optional)

```json
// Message history acknowledgment
{ "status": "ok", "type": "message_history_ack", "message_count": 5 }

// Message stream acknowledgment (for is_final: true)
{ "status": "ok", "type": "message_stream_ack" }

// Session end acknowledgment
{ "status": "ok", "type": "session_end_ack", "message": "Session ended", "saved_count": 10 }
```

### Backend Example (Node.js)

```javascript
const WebSocket = require('ws');
const axios = require('axios');

const wss = new WebSocket.Server({ port: 8000 });

wss.on('connection', (ws) => {
  ws.on('message', async (message) => {
    const data = JSON.parse(message);
    
    // Handle session request
    if (data.user_trigger !== undefined) {
      const startTime = Date.now();
      const session = await createAnamSession(data.userId, data.skip_greeting);
      
      ws.send(JSON.stringify({
        status: 'success',
        message: 'Session created',
        session_data: {
          session_id: session.id,
          token: session.sessionToken,
          expires_at: session.expiresAt,
        },
        time_taken: Date.now() - startTime,
      }));
    }
    
    // Handle message history (store in database)
    if (data.type === 'message_history') {
      await storeMessages(data.session_id, data.user_id, data.messages);
      ws.send(JSON.stringify({
        status: 'ok',
        type: 'message_history_ack',
        message_count: data.messages.length,
      }));
    }
    
    // Handle session end
    if (data.type === 'session_end') {
      await finalizeSession(data.session_id, data.user_id);
      ws.send(JSON.stringify({
        status: 'ok',
        type: 'session_end_ack',
        message: 'Session ended',
        saved_count: 10,
      }));
    }
  });
});

async function createAnamSession(userId, skipGreeting = false) {
  const response = await axios.post(
    'https://api.anam.ai/v1/auth/session-token',
    {
      personaConfig: {
        name: 'Aria',
        avatarId: 'your-avatar-id',
        voiceId: 'your-voice-id',
        llmId: 'your-llm-id',
        systemPrompt: 'You are Aria, a helpful AI assistant.',
        skip_greeting: skipGreeting,  // Pass skip_greeting to Anam
      },
    },
    {
      headers: { 'Authorization': `Bearer ${process.env.ANAM_API_KEY}` },
    }
  );
  
  return response.data;
}

async function storeMessages(sessionId, userId, messages) {
  // Store messages in your database
  console.log(`Storing ${messages.length} messages for user ${userId}`);
}

async function finalizeSession(sessionId, userId) {
  // Mark session as complete, perform any cleanup
  console.log(`Session ${sessionId} ended for user ${userId}`);
}
```

## Session Lifecycle

The SDK manages Anam sessions automatically:

| Event | What Happens |
|-------|--------------|
| **Widget Opens (first time)** | Sends `user_trigger` with `skip_greeting: false` → Creates Anam session with greeting |
| **Widget Closes (hang up)** | Sends `session_end` → Ends Anam session to free resources |
| **Widget Reopens** | Sends `user_trigger` with `skip_greeting: true` → Creates new session without greeting |
| **Page Refresh** | Sends `user_trigger` with `skip_greeting: false` → Fresh session with greeting |

This ensures:
- Anam resources are freed when the widget is closed
- Users don't hear the greeting repeatedly when reopening the widget
- Conversation context can be restored via your backend's message storage

## Session Persistence

For video sessions to persist across page navigation, initialize the SDK at the **root level** of your application:

```tsx
// App.tsx - Correct placement
import { Aria } from '@centive/aria-sdk';

// Initialize once at app startup
Aria.init({
  websocketUrl: 'wss://your-server.com/ws',
  userId: 'user_123',
});

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/dashboard" element={<DashboardPage />} />
      </Routes>
    </Router>
  );
}
```

## Bundler Configuration

The `@anam-ai/js-sdk` requires a Buffer polyfill. See the [Integration Guide](./INTEGRATION.md#3-configure-your-bundler) for Vite, Webpack, and Next.js configurations.

**Quick fix for Vite:**

```bash
npm install buffer
```

```typescript
// main.tsx (at the very top)
import { Buffer } from 'buffer';
globalThis.Buffer = Buffer;
```

## Security Best Practices

- Use secure WebSocket connections (`wss://`) in production
- Store Anam API keys only on the backend
- Validate user IDs on your backend
- Never expose API keys in frontend code

## Browser Support

- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
- Opera (latest)

Requires WebRTC support for video streaming.

## Documentation

- [Integration Guide](./INTEGRATION.md) - Complete integration documentation
- [WebSocket Events](./WEBSOCKET_EVENTS.md) - Message specifications
- [Type Reference](./TYPE_REFERENCE.md) - TypeScript types
- [Anam AI Docs](https://docs.anam.ai/) - Platform documentation

## License

MIT © Centive

## Support

- [GitHub Issues](https://github.com/centive/aria-sdk/issues)
- [Anam AI Documentation](https://docs.anam.ai/)
