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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@applica-software-guru/persona-sdk

v0.1.89

Published

[![npm version](https://img.shields.io/npm/v/@applica-software-guru/persona-sdk)](https://www.npmjs.com/package/@applica-software-guru/persona-sdk) [![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](#) [![Documentation](https://img.

Readme

Persona SDK ⚙️

npm version Build Status Documentation

Persona SDK is the official SDK for the Persona API. It provides a simple and efficient way to integrate the Persona API into your applications using assistant-ui.


Table of Contents


Features

  • Custom Runtime Provider: Easily integrate the Persona API with assistant-ui chat.
  • Local Runtime Support: Run the assistant-ui chat locally using the Persona API.
  • Multiple Protocols: Supports REST, WebSocket, and WebRTC protocols.
  • WebRTC Optimization: Communicate with AI agents in real-time using the fastest protocol available.
  • Customizable: Extend functionality with custom protocols and loggers.

Installation

To get started, install the Persona SDK using npm or yarn:

npm install @applica-software-guru/persona-sdk

If you don't already have an assistant-ui-based project, you can create one using the Assistant UI Getting Started Guide.


Usage

Here’s an example of how to use the Persona SDK in your chat application:

import dynamic from 'next/dynamic';

const PersonaRuntimeProvider = dynamic(() => import('@applica-software-guru/persona-sdk').then((mod) => mod.PersonaRuntimeProvider), {
  ssr: false,
});

const logger = new PersonaConsoleLogger();

function Chat() {
  return (
    <PersonaRuntimeProvider
      dev
      logger={logger}
      protocols={{
        rest: true,
        webrtc: true,
        websocket: true,
      }}
      session={'<session-id>'}
      apiKey="<api-key>"
      agentId={'<agent-name>'}
    >
      <div className="h-dvh w-full">
        <Thread />
      </div>
    </PersonaRuntimeProvider>
  );
}

Note: When using WebRTC, it is recommended to import PersonaRuntimeProvider dynamically with ssr: false (as shown above) to ensure compatibility with server-side rendering frameworks like Next.js.

Development Mode

Use the dev prop to enable development mode. This redirects all traffic to a local installation of Persona (e.g., localhost:8000).


Accessing Messages

usePersonaRuntimeMessages

The usePersonaRuntimeMessages hook provides a simple way to access the current list of messages managed by the Persona SDK runtime. This hook returns an array of PersonaMessage objects, which represent the conversation history, including user, assistant, and reasoning messages.

Usage Example:

import { usePersonaRuntimeMessages } from '@applica-software-guru/persona-sdk';

function MessageLogger() {
  const messages = usePersonaRuntimeMessages();
  return (
    <div>
      <h3>Messages</h3>
      <pre>{JSON.stringify(messages, null, 2)}</pre>
    </div>
  );
}

This hook must be used within a PersonaRuntimeProvider.

usePersonaRuntime and getMessages

The usePersonaRuntime hook gives you access to the Persona runtime context, which now includes a getMessages method. This method returns the current array of PersonaMessage objects, allowing you to programmatically retrieve the latest messages at any time.

Usage Example:

import { usePersonaRuntime } from '@applica-software-guru/persona-sdk';

function MyComponent() {
  const { getMessages } = usePersonaRuntime();
  const messages = getMessages();
  // Use messages as needed
}

Both approaches are useful for building custom UI components, analytics, or debugging tools that need access to the chat history.


Message Filtering

The messageFilter prop allows you to process and filter messages before they are displayed in the chat interface. This provides powerful customization capabilities for controlling what messages users see and how they are presented.

Basic Usage

Add a messageFilter function to your PersonaRuntimeProvider:

import { PersonaRuntimeProvider, MessageFilter, PersonaMessage } from '@applica-software-guru/persona-sdk';

const myMessageFilter: MessageFilter = (messages: PersonaMessage[]) => {
  // Process and return the filtered messages
  return messages.filter((message) => message.type !== 'reasoning');
};

function App() {
  return (
    <PersonaRuntimeProvider apiKey="your-api-key" agentId="your-agent-id" protocols={{ rest: true }} messageFilter={myMessageFilter}>
      {/* Your app content */}
    </PersonaRuntimeProvider>
  );
}

MessageFilter Type

type MessageFilter = (messages: PersonaMessage[]) => PersonaMessage[];

The function receives an array of PersonaMessage objects and must return an array of PersonaMessage objects.

Common Use Cases

1. Hide Reasoning Messages

const hideReasoningFilter: MessageFilter = (messages) => {
  return messages.filter((message) => message.type !== 'reasoning');
};

2. Content Filtering

const profanityFilter: MessageFilter = (messages) => {
  const bannedWords = ['spam', 'inappropriate'];
  return messages.filter((message) => {
    const text = message.text.toLowerCase();
    return !bannedWords.some((word) => text.includes(word));
  });
};

3. Message Limit

const messageLimitFilter: MessageFilter = (messages) => {
  const maxMessages = 50;
  return messages.length > maxMessages ? messages.slice(-maxMessages) : messages;
};

4. Text Transformation

const transformTextFilter: MessageFilter = (messages) => {
  return messages.map((message) => ({
    ...message,
    text: message.text.replace(/\b(TODO|FIXME)\b/g, '⚠️ $1'),
  }));
};

5. Combining Multiple Filters

const combinedFilter: MessageFilter = (messages) => {
  let filtered = hideReasoningFilter(messages);
  filtered = profanityFilter(filtered);
  filtered = messageLimitFilter(filtered);
  return filtered;
};

Important Notes

  • The filter is applied to all messages, including both incoming messages from the assistant and outgoing messages from the user
  • The filter function should be pure (no side effects) and return a new array
  • Filters are applied every time the message list is updated
  • If no filter is provided, all messages are displayed as-is
  • Be careful with message ordering when filtering, as it may affect conversation context

Performance Considerations

  • Keep filter functions lightweight as they run on every message update
  • For expensive operations, consider debouncing or memoization
  • Avoid creating new filter functions on every render to prevent unnecessary re-processing

For complete working examples, see the examples/message-filter-example.tsx file in the repository.


Message Storage

The Persona SDK provides a flexible message storage system that allows you to persist conversation history across sessions. By default, messages are stored in memory, but you can easily switch to localStorage or implement your own custom storage solution.

Storage Implementations

InMemoryMessageStorage (Default)

Messages are stored in memory and lost on page reload. This is the default if no storage is specified.

import { PersonaRuntimeProvider, InMemoryMessageStorage } from '@applica-software-guru/persona-sdk';

const storage = new InMemoryMessageStorage();

<PersonaRuntimeProvider apiKey="your-api-key" agentId="your-agent-id" protocols={{ rest: true }} threads={true} messageStorage={storage}>
  {/* Your app */}
</PersonaRuntimeProvider>;

LocalStorageMessageStorage

Messages persist across page reloads using browser localStorage.

import { PersonaRuntimeProvider, LocalStorageMessageStorage } from '@applica-software-guru/persona-sdk';

const storage = new LocalStorageMessageStorage('my_app_messages_');

<PersonaRuntimeProvider apiKey="your-api-key" agentId="your-agent-id" protocols={{ rest: true }} threads={true} messageStorage={storage}>
  {/* Your app */}
</PersonaRuntimeProvider>;

Custom Storage Implementation

You can implement your own storage solution by implementing the MessageStorage interface:

import { MessageStorage, PersonaMessage, Session } from '@applica-software-guru/persona-sdk';

class CustomMessageStorage implements MessageStorage {
  async saveMessages(sessionId: Session, messages: PersonaMessage[]): Promise<void> {
    // Your custom save logic (e.g., IndexedDB, remote API)
    await fetch('/api/save-messages', {
      method: 'POST',
      body: JSON.stringify({ sessionId, messages }),
    });
  }

  async loadMessages(sessionId: Session): Promise<PersonaMessage[]> {
    // Your custom load logic
    const response = await fetch(`/api/load-messages/${sessionId}`);
    return await response.json();
  }

  async deleteMessages(sessionId: Session): Promise<void> {
    // Your custom delete logic
    await fetch(`/api/delete-messages/${sessionId}`, { method: 'DELETE' });
  }

  async clearAll(): Promise<void> {
    // Clear all messages
    await fetch('/api/clear-all-messages', { method: 'DELETE' });
  }

  async getAllSessionIds(): Promise<Session[]> {
    // Get all session IDs
    const response = await fetch('/api/session-ids');
    return await response.json();
  }
}

const customStorage = new CustomMessageStorage();

<PersonaRuntimeProvider
  messageStorage={customStorage}
  // ... other props
/>;

How It Works

When thread management is enabled (threads={true}):

  • Each thread has its own session ID
  • Messages are automatically saved to storage when updated
  • When switching threads, messages are loaded from storage
  • This allows seamless conversation persistence across sessions

Protocols Overview

The Persona SDK supports multiple communication protocols to provide flexibility and performance. These protocols can work together, sharing the same session and data, to ensure the best possible experience.

REST

  • Description: A simple and reliable protocol for sending and receiving data.
  • Use Case: Ideal for applications where real-time communication is not critical.

WebSocket

  • Description: A full-duplex communication protocol that enables real-time interaction between the client and server.
  • Use Case: Suitable for scenarios requiring low-latency communication, such as live chat applications.

WebRTC

  • Description: A peer-to-peer communication protocol designed primarily for ultra-fast audio sessions, while also supporting high-speed messaging with minimal latency.
  • Use Case: Perfect for real-time, interactive applications where rapid audio communication and responsiveness are critical.
  • Why WebRTC?: By creating direct peer-to-peer connections, WebRTC eliminates unnecessary server delays, ensuring the fastest possible audio sessions and seamless message exchanges.

Pro Tip: For optimal audio performance, make sure WebRTC is enabled in the protocols configuration of your application.


How Protocols Work Together

The Persona SDK runtime is designed to use multiple protocols simultaneously, ensuring flexibility and reliability. Here’s how it works:

  1. Shared Session: All protocols share the same session and operate on the same data, ensuring consistency across communication channels.
  2. Protocol Selection: The runtime automatically selects the fastest and most reliable protocol from the list of enabled protocols. For example:
    • If WebRTC is available and started, it will be prioritized for its ultra-low latency.
    • If WebRTC is not available, WebSocket will be used for real-time communication.
    • REST will act as a fallback for non-real-time communication.
  3. WebRTC Startup: WebRTC requires manual startup by the user (e.g., clicking a microphone icon to enable audio/video). Once started, WebRTC participates in the list of reliable protocols and is used for the fastest communication.
  4. Seamless Integration: Even if WebRTC is not started, other protocols like WebSocket and REST will continue to function, ensuring uninterrupted communication.

This design allows the Persona SDK to adapt dynamically to the available protocols, providing the best possible performance and reliability.


API Reference

Props for PersonaRuntimeProvider

| Prop | Type | Default Value | Description | | --------------- | ------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------- | | dev | boolean | false | Enable development mode for local Persona API usage. | | logger | function | console.log | Custom logger for debugging and logging messages. | | protocols | object | { rest: true, webrtc: true, websocket: true } | Protocols to use for the chat. Can be enabled/disabled using boolean values. | | session | string | undefined | Session ID for the chat. Required for the chat to function. | | apiKey | string | toolkit-test | API key for authentication. Required for the chat to function. | | agentId | string | default | Agent ID for the chat. Required for the chat to function. | | messageFilter | MessageFilter | undefined | Optional function to filter and transform messages before they are displayed in the chat interface. |


Customization

The Persona SDK allows you to extend its functionality by creating custom protocols and loggers.

Custom Protocols

You can implement your own protocol by extending the PersonaProtocolBase class. For example:

import { PersonaProtocolBase } from '@applica-software-guru/persona-sdk';

class CustomProtocol extends PersonaProtocolBase {
  // Implement required methods like connect, disconnect, send, etc.
}

Example: Custom Protocol Implementation

Here’s an example of a custom protocol named CustomProtocol:

import { PersonaProtocolBase } from '@applica-software-guru/persona-sdk';

class CustomProtocol extends PersonaProtocolBase {
  public getName(): string {
    return 'custom';
  }

  public async connect(): Promise<void> {
    console.log('[CustomProtocol] Connecting...');
    this.setStatus('connected');
  }

  public async disconnect(): Promise<void> {
    console.log('[CustomProtocol] Disconnecting...');
    this.setStatus('disconnected');
  }

  public async send(message: string): Promise<void> {
    console.log(`[CustomProtocol] Sending message: ${message}`);
    this.notifyMessage({ role: 'assistant', type: 'text', text: `Echo: ${message}` });
  }
}

Custom Loggers

You can create a custom logger by implementing the PersonaLogger interface:

import { PersonaLogger } from '@applica-software-guru/persona-sdk';

class CustomLogger implements PersonaLogger {
  log(message: string, ...args: unknown[]): void {
    console.log(`[CustomLogger] ${message}`, ...args);
  }

  info(message: string, ...args: unknown[]): void {
    console.info(`[CustomLogger] ${message}`, ...args);
  }

  warn(message: string, ...args: unknown[]): void {
    console.warn(`[CustomLogger] ${message}`, ...args);
  }

  error(message: string, ...args: unknown[]): void {
    console.error(`[CustomLogger] ${message}`, ...args);
  }

  debug(message: string, ...args: unknown[]): void {
    console.debug(`[CustomLogger] ${message}`, ...args);
  }
}

Transaction Protocol

The Transaction Protocol in the Persona SDK is designed to handle structured interactions between the client and the Persona API. It allows you to define and execute specific operations (tools) in response to function calls.

How It Works

  1. Transaction Handling: The protocol listens for incoming transactions and invokes the appropriate tool based on the function call.
  2. Session Management: It shares the same session as other protocols, ensuring consistency.
  3. Custom Logic: You can define custom tools to handle specific function calls.

Usage Example

To enable the Transaction Protocol, include it in the protocols configuration of the PersonaRuntimeProvider:

<PersonaRuntimeProvider
  dev
  logger={logger}
  protocols={{
    transaction: async (transaction) => {
      await transaction.invoke({
        get_user_agent: () => navigator.userAgent || 'unknown',
        get_client_date: () => ({ date: new Date().toISOString() }),
      });
    },
  }}
  session="your-session-id"
  apiKey="your-api-key"
  agentId="your-agent-id"
>
  <YourComponent />
</PersonaRuntimeProvider>

In this example:

  • The transaction protocol is configured with a callback function.
  • The callback defines tools (get_user_agent and get_client_date) that the protocol can invoke.

Using the tools Prop in PersonaRuntimeProvider

The tools prop allows you to define custom tools that the Transaction Protocol can invoke in response to function calls. As of vNEXT, you can provide tools in two ways:

1. As an Object (Legacy, Backward Compatible)

Important: When using the legacy object format, your agent configuration must also include the tool schema for each tool. If the schema is missing, the configured tools will not work as expected. This is not required when using the array format with createTool.

You can define tools as an object where each key is the tool's name and the value is the function to execute:

<PersonaRuntimeProvider
  tools={{
    get_user_agent: () => navigator.userAgent || 'unknown',
    get_client_date: () => ({ date: new Date().toISOString() }),
  }}
  ...otherProps
/>

2. As an Array of Tool Instances (Recommended)

For better type safety, schema support, and documentation, you can define tools using the createTool utility and pass an array of tool instances:

import { createTool } from '@applica-software-guru/persona-sdk';

const tools = [
  createTool({
    name: 'sum',
    title: 'Sum two numbers',
    description: 'Sums two numbers and returns the result.',
    parameters: {
      a: { type: 'number', description: 'First number to sum', required: true },
      b: { type: 'number', description: 'Second number to sum', required: true },
    },
    output: {
      result: { type: 'number', description: 'Sum of the two numbers' },
    },
    implementation: async (a: number, b: number) => {
      return { result: a + b };
    },
  }),
  // Add more tools as needed
];

<PersonaRuntimeProvider
  tools={tools}
  ...otherProps
/>

Recommended: Use the array format with createTool for new projects. This enables better validation, documentation, and future extensibility.

How It Works

  • The SDK will automatically normalize the tools you provide, regardless of the format.
  • When a transaction is received, the Transaction Protocol invokes the appropriate tool by name.
  • Both synchronous and asynchronous tool functions are supported.

Best Practices

  • Prefer the array format with createTool for new codebases.
  • Use descriptive names and provide parameter/output schemas for each tool.
  • Handle errors gracefully in your tool implementations.

Managing Tools

Tools are custom functions that the Transaction Protocol can invoke in response to specific function calls. They allow you to extend the SDK's functionality by defining your own operations.

Defining Tools

Tools are defined as key-value pairs, where the key is the tool's name, and the value is the function to execute.

Example:

const tools = {
  get_user_agent: () => navigator.userAgent || 'unknown',
  get_client_date: () => ({ date: new Date().toISOString() }),
};

Using Tools in Transactions

When a transaction is received, the protocol matches the function call's name with the tool's name and executes the corresponding function.

Example:

transaction.invoke(tools);

Error Handling

If a tool is not found or an error occurs during execution, the transaction will fail. You can handle these scenarios by implementing error handling in your tools.

Example:

const tools = {
  risky_tool: () => {
    try {
      // Perform some operation
      return { success: true };
    } catch (error) {
      throw new Error('Tool execution failed');
    }
  },
};

Enhanced Tools Support

The tools prop now supports both synchronous and asynchronous tools. For example:

<PersonaRuntimeProvider
  tools={{
    get_user_agent_async: async () => {
      const userAgent = navigator.userAgent || 'unknown';
      return { userAgent };
    },
    get_user_agent: () => {
      const userAgent = navigator.userAgent || 'unknown';
      return { userAgent };
    },
  }}
  ...otherProps
/>

This allows for greater flexibility in defining tools that may require asynchronous operations.


Best Practices

  1. Tool Naming: Use descriptive names for tools to make them easy to identify.
  2. Error Handling: Ensure tools handle errors gracefully to avoid unexpected failures.
  3. Testing: Test tools thoroughly to ensure they work as expected in different scenarios.
  4. Security: Avoid exposing sensitive data or performing unsafe operations in tools.

By leveraging the Transaction Protocol and tools, you can create a highly customizable and extensible integration with the Persona SDK.


Advanced Transaction Handling

In addition to using the tools prop for automatic transaction handling, you can manage transactions manually by inspecting the function call and executing the complete or fail methods of the persistable entity passed to the transaction handler.

Manual Transaction Handling

When handling transactions manually, you gain full control over the transaction lifecycle. This allows you to inspect the function call, perform custom logic, and explicitly mark the transaction as complete or failed.

Example Usage

Here’s an example of how to handle transactions manually:

<PersonaRuntimeProvider
  dev
  logger={logger}
  protocols={{
    transaction: async (transaction) => {
      const { functionCall, persistable } = transaction;

      try {
        // Inspect the function call
        if (functionCall.name === 'get_user_agent') {
          const result = navigator.userAgent || 'unknown';
          await persistable.complete(result);
        } else if (functionCall.name === 'get_client_date') {
          const result = { date: new Date().toISOString() };
          await persistable.complete(result);
        } else {
          // Handle unknown function calls
          throw new Error(`Unknown function call: ${functionCall.name}`);
        }
      } catch (error) {
        // Mark the transaction as failed
        await persistable.fail({ error: error.message });
      }
    },
  }}
  session="your-session-id"
  apiKey="your-api-key"
  agentId="your-agent-id"
>
  <YourComponent />
</PersonaRuntimeProvider>

How It Works

  1. Inspect Function Call: The functionCall object contains details about the function being invoked, such as its name and arguments.
  2. Custom Logic: Based on the function call, you can perform custom logic to determine the appropriate response.
  3. Complete or Fail: Use the complete method to return a successful result or the fail method to indicate an error.

Benefits

  • Flexibility: Allows you to handle complex scenarios that may not be possible with predefined tools.
  • Error Handling: Provides a mechanism to gracefully handle errors and unknown function calls.

Best Practices

  • Always inspect the functionCall object to ensure you are handling the correct function.
  • Use descriptive error messages when calling the fail method to make debugging easier.
  • Test your transaction handler thoroughly to ensure it works as expected in all scenarios.

By combining manual transaction handling with the tools prop, you can create a robust and flexible integration with the Persona SDK.


Contributing

We welcome contributions! To get started:

  1. Fork the repository.
  2. Create a new branch for your feature or bugfix.
  3. Submit a pull request with a detailed description of your changes.

Please ensure your code adheres to the existing style and passes all linting and testing checks.


License

This project is licensed under the MIT License. See the LICENSE file for details.


Support

For questions or support, contact us at [email protected].


Acknowledgments

This SDK is built on top of assistant-ui, a powerful framework for building conversational interfaces.