# Obi SDK

A JavaScript SDK with dynamic content capabilities for integrating Obi into your web applications.

## Features

- Assistant interacts through voice and screen share
- DOM analysis allows the assistant to improve context
- Framework-agnostic UI components using Web Components
- Event-based API
- TypeScript support
- Session persistence across page reloads

## Configuration Options

The loader accepts the following configuration options:

| Option           | Type    | Default          | Description                                                                                                                                                                       |
| ---------------- | ------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `position`       | string  | `'bottom-right'` | Position of the widget on the page. Options: `'bottom-right'`, `'bottom-left'`, `'bottom-center'`, `'top-right'`, `'top-left'`, `'top-center'`, `'middle-left'`, `'middle-right'` |
| `apiKey`         | string  | -                | Your Obi API key (required for production)                                                                                                                                        |
| `user`           | object  | -                | User information with `id` (required), `email`, `first_name`, `last_name`, `company` (all optional), and `metadata` (optional) for additional custom fields                       |
| `isActive`       | boolean | `true`           | Whether to show the widget. Set to `false` to disable the widget for specific users                                                                                               |
| `linkOnlyAccess` | boolean | `false`          | Hide the widget unless accessed via a session link. Perfect for trial periods where only customers with direct links should see the widget                                        |
| `primaryColor`   | string  | -                | Custom primary color for the widget UI (CSS color value)                                                                                                                          |
| `autoOpenPlanUuid` | string | -              | Automatically open the start-session popup for a specific plan after the widget mounts                                                                                             |

## Installation

For installation instructions, we recommend using the **obi-loader** package. This package provides a lightweight loader that will automatically fetch and initialize the full and latest SDK.

Please visit the [obi-loader npm package](https://www.npmjs.com/package/obi-loader) for installation methods and documentation.

## Widget Positioning

The widget can be positioned in one of eight positions on the page:

```js
window.obiWidgetConfig = {
  position: "bottom-right", // Options: "bottom-right", "bottom-left", "bottom-center", "top-right", "top-left", "top-center", "middle-left", "middle-right"
  apiKey: "YOUR_API_KEY",
}
```

## Trial Mode / Link-Only Access

The `linkOnlyAccess` option is perfect for trial periods where you want to control who can see and interact with the widget. When enabled, the widget remains hidden until a session is started via a direct link.

```js
window.obiWidgetConfig = {
  apiKey: "YOUR_API_KEY",
  linkOnlyAccess: true, // Widget only visible when accessed via session link
}
```

This is ideal for:

- Trial periods where only specific customers should see the widget
- Beta testing with selected users
- Controlled rollouts using session-specific links
- Demo environments where widget access is link-based

When `linkOnlyAccess` is `true`:

- Widget is invisible to regular website visitors
- Widget becomes visible only when a session is started (e.g., via URL parameters)
- Once a session ends, the widget returns to hidden state

## Session Persistence

The SDK now supports session persistence across page reloads and navigation. If a user refreshes the page or navigates to another page on your site, the session with Obi will be automatically restored when they return, providing a seamless experience.

Key features of session persistence:

- Sessions are stored locally in the browser using a namespaced localStorage implementation
- Session data is automatically cleared after a configurable expiration time (default: 5 minutes)
- When reconnecting, the previous session state is restored, including the current conversation
- Users can manually end a session at any time by using the disconnect option

No additional configuration is required to enable this feature.

## Command API

The Obi SDK provides a programmatic command API for controlling sessions and interactions. While most users should use the [obi-loader](https://www.npmjs.com/package/obi-loader) package which provides a convenient wrapper, advanced users can access the SDK instance directly via `window.obi`.

### Using window.ObiSDK() (Recommended)

For most use cases, use the global `window.ObiSDK()` function provided by the obi-loader:

```typescript
// Start a session
window.ObiSDK("startSession", { planUuid: "plan-uuid", withMicrophone: true })

// Send a message
window.ObiSDK("say", "Help me with this feature")

// Stop a session
window.ObiSDK("stopSession")

// Update configuration
window.ObiSDK("update", { showMenu: true })
```

For detailed documentation and examples of all commands, see the [obi-loader README](https://www.npmjs.com/package/obi-loader#programmatic-commands).

### Advanced: Direct SDK Instance Usage

Advanced users can access the SDK instance directly for more control:

```typescript
// Access the SDK instance
const sdk = window.obi

// Start a session
await sdk.startSession({ withMicrophone: true })

// Send a message
await sdk.say("I need help with this feature")

// Stop the session
sdk.client?.stopSession()

// Update configuration
sdk.updateConfig({ showMenu: true, isActive: true })
```

### Available Commands

| Command        | Description                                    | Parameters                                              |
| -------------- | ---------------------------------------------- | ------------------------------------------------------- |
| `update`       | Update widget configuration                    | `config: Partial<Config>` - Configuration object        |
| `startSession` | Start an onboarding session                    | `options?: { planUuid?: string, withMicrophone?: boolean }` |
| `say`          | Send a text message to the agent               | `message: string` - Message to send                     |
| `stopSession`  | Stop the current session                       | None                                                    |

**Note**: For comprehensive documentation, usage examples, and best practices, refer to the [obi-loader package documentation](https://www.npmjs.com/package/obi-loader).

## Event API

The Obi SDK extends `EventEmitter` and provides events for monitoring SDK state and responding to changes. Access the SDK instance via `window.obi` to subscribe to events.

### Available Events

```typescript
// Listen for SDK initialization
window.obi.on("initialised", () => {
  console.log("Obi SDK is ready")
  // Perform actions after SDK is initialized
})

// Handle errors
window.obi.on("error", (error: Error) => {
  console.error("Obi SDK error:", error)
  // Report to error tracking service
  errorTracker.captureException(error)
})

// Monitor menu visibility changes
window.obi.on("showMenu", (isVisible: boolean) => {
  console.log(`Menu is now ${isVisible ? "visible" : "hidden"}`)
  // Track analytics
  analytics.track("obi_menu_toggled", { visible: isVisible })
})

// Track configuration updates
window.obi.on("configUpdated", () => {
  console.log("Obi configuration has been updated")
  // Sync with your application state
})

// Handle transient messages
window.obi.on("showMessage", ({ text, autoCloseMs }) => {
  console.log(`Message: ${text} (auto-close in ${autoCloseMs}ms)`)
  // Display in custom UI if needed
})
```

### Event Reference

| Event            | Parameters                                | Description                                                    |
| ---------------- | ----------------------------------------- | -------------------------------------------------------------- |
| `initialised`    | None                                      | Fired when SDK initialization is complete                      |
| `error`          | `error: Error`                            | Fired when an error occurs in the SDK                          |
| `showMenu`       | `isVisible: boolean`                      | Fired when the widget menu visibility changes                  |
| `configUpdated`  | None                                      | Fired when the SDK configuration is updated                    |
| `showMessage`    | `{ text: string, autoCloseMs?: number }`  | Fired when a transient message should be displayed to the user |

### Use Cases

**Error Tracking Integration:**

```typescript
window.obi.on("error", (error) => {
  // Send to Sentry, Bugsnag, or other error tracking service
  Sentry.captureException(error, {
    tags: { source: "obi-sdk" },
  })
})
```

**Analytics Integration:**

```typescript
window.obi.on("initialised", () => {
  analytics.track("obi_sdk_loaded")
})

window.obi.on("showMenu", (isVisible) => {
  analytics.track("obi_menu_interaction", {
    action: isVisible ? "opened" : "closed",
    timestamp: new Date().toISOString(),
  })
})
```

**UI Synchronization:**

```typescript
// Sync menu state with your application UI
window.obi.on("showMenu", (isVisible) => {
  document.getElementById("obi-menu-indicator")?.classList.toggle("active", isVisible)
})

// Display custom notifications
window.obi.on("showMessage", ({ text, autoCloseMs }) => {
  showCustomToast(text, autoCloseMs || 5000)
})
```

**State Management Integration:**

```typescript
// Redux example
window.obi.on("configUpdated", () => {
  store.dispatch(obiConfigUpdated())
})

window.obi.on("error", (error) => {
  store.dispatch(obiError(error.message))
})

// React state example
function useObiEvents() {
  const [isMenuVisible, setIsMenuVisible] = useState(false)
  const [isInitialized, setIsInitialized] = useState(false)

  useEffect(() => {
    const sdk = window.obi

    sdk.on("initialised", () => setIsInitialized(true))
    sdk.on("showMenu", setIsMenuVisible)

    return () => {
      sdk.off("initialised")
      sdk.off("showMenu")
    }
  }, [])

  return { isMenuVisible, isInitialized }
}
```

### TypeScript Support

The SDK provides full TypeScript support for events:

```typescript
import type { ObiSDK, ObiSDKEvents } from "@obi/obi-sdk"

// Event handlers are fully typed
window.obi.on("initialised", () => {
  // No parameters
})

window.obi.on("error", (error: Error) => {
  // error is typed as Error
})

window.obi.on("showMenu", (isVisible: boolean) => {
  // isVisible is typed as boolean
})

window.obi.on("showMessage", ({ text, autoCloseMs }) => {
  // Parameters are typed correctly
})
```

## Engagement Sessions

The SDK also tracks lightweight engagement sessions per product + onboardee for analytics. A new engagement session starts after 30 minutes of inactivity and is stored in `localStorage`. You can subscribe to the `engagementSessionStarted` event and read current values via `getEngagementInfo()` on the SDK instance (available as `window.obi` when using the loader).

- Event: `engagementSessionStarted` with `{ sessionCount, lastSessionStartAt, lastActivityAt, scopeKey }`.
- Accessor: `sdk.getEngagementInfo()` returns the latest stored info for the current scope.
- Blacklist: URLs matching the product blacklist do not count toward engagement sessions.
- Cross-tab: Duplicate suppression, if enabled, is best-effort within a small window.

See architecture details and examples in STRUCTURE.md under “Engagement Sessions”: `STRUCTURE.md#engagement-sessions`.

## License

SEE LICENSE IN LICENSE.txt
