# Messages and Storage

## Accessing Messages

### usePersonaRuntimeMessages

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

**Usage Example:**

```tsx
import { usePersonaRuntimeMessages } from '@applica-software-guru/persona-chat-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:**

```tsx
import { usePersonaRuntime } from '@applica-software-guru/persona-chat-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.

---

## System Notices

Some messages are produced by the runtime itself, not by the agent: the turn was
interrupted by the user (stop button, `cancel`) or it ended with an error. They
are appended to the conversation like any other message, but carry a marker:

```ts
{
  role: 'assistant',
  type: 'text',
  text: 'Task interrupted',          // fallback text
  status: { type: 'incomplete' },
  metadata: { custom: { notice: 'cancelled' } },   // 'cancelled' | 'error'
}
```

Check `metadata.custom.notice` to render them as a notice rather than as an
agent reply (persona-chat draws a thin centered line in the thread). The marker
is also what keeps these messages from being merged into the (possibly
truncated) reply that precedes them.

The notice is added once per run: `agent_end` can arrive twice for the same
turn — reconnecting to a session replays the events already emitted — and the
duplicate is ignored.

## 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`:

```tsx
import { PersonaRuntimeProvider, MessageFilter, PersonaMessage } from '@applica-software-guru/persona-chat-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

```typescript
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

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

#### 2. Content Filtering

```tsx
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

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

#### 4. Text Transformation

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

#### 5. Combining Multiple Filters

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

---

## Message Storage

The Persona Chat 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.

```tsx
import { PersonaRuntimeProvider, InMemoryMessageStorage } from '@applica-software-guru/persona-chat-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.

```tsx
import { PersonaRuntimeProvider, LocalStorageMessageStorage } from '@applica-software-guru/persona-chat-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:

```tsx
import { MessageStorage, PersonaMessage, Session } from '@applica-software-guru/persona-chat-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
/>;
```
