<!-- source-hash: c1d1e8fb922837f8d57888644f4624f5 -->
Manages the client-side chat state for an AI assistant conversation, handling message streaming, tool approval workflows, and adaptive thinking display via SSE.

## Key Components

### `UseChatOptions`
Configuration interface for the hook:
- `useMock` / `debugMode` — toggle mock/debug SSE modes
- `assistantName` / `assistantAvatar` — customize assistant identity
- `streamFn` — override default SSE transport with a custom function
- `initialMessages` — seed conversation state (e.g., from `localStorage`)
- `onMessagesChange` — persistence callback triggered on every message update

### `useChat(options)`
Primary hook returning:

| Return Value | Type | Description |
|---|---|---|
| `messages` | `Message[]` | Full conversation history |
| `isTyping` | `boolean` | True until first text chunk arrives |
| `isStreaming` | `boolean` | True while SSE stream is active |
| `sseError` | `Error \| null` | Stream-level error state |
| `sendMessage` | `fn` | Send a user turn, optionally hidden |
| `abort` | `fn` | Cancel the active stream |
| `reset` | `fn` | Reset SSE state |

### `sendMessage(text, extra?, options?)`
Appends a user message, creates an optimistic assistant placeholder, then iterates the SSE stream, handling three segment types:
- **`text`** — accumulates into the last assistant message
- **`thinking`** — prepended as a thinking card (always before answer text)
- **`approval_request`** — renders a tool-approval card with `onApprove`/`onReject` callbacks
- **`decision_resolved`** — flips approval card status and appends a receipt to the current turn

## Usage Example

```typescript
import { useChat } from './use-chat'

function ChatWidget() {
  const { messages, isTyping, sendMessage, abort } = useChat({
    assistantName: 'Mingo',
    initialMessages: JSON.parse(localStorage.getItem('chat') ?? '[]'),
    onMessagesChange: (msgs) => localStorage.setItem('chat', JSON.stringify(msgs)),
    streamFn: myCustomStreamFn, // optional: bypass default SSE
  })

  return (
    <div>
      {messages.map((m) => <MessageBubble key={m.id} message={m} />)}
      {isTyping && <TypingIndicator />}
      <button onClick={() => sendMessage('Hello!')}>Send</button>
      <button onClick={abort}>Stop</button>
    </div>
  )
}
```

> **Hidden turns**: Pass `{ hidden: true }` to `sendMessage` for post-approval auto-continuation — the user message is added to LLM context but not rendered in the UI, keeping the visible thread clean.