# Sessions

A session is the primary container for a conversation with Navi. It persists in the database and survives server restarts.

## Session fields

| Field | Type | Description |
|-------|------|-------------|
| `session_id` | UUID string | Unique identifier |
| `profile_id` | string | Active profile (can change via `switch_profile`) |
| `created_at` | ISO 8601 | Creation time |
| `last_active` | ISO 8601 | Last activity time |
| `pinned` | bool | Pinned sessions appear first in lists |
| `message_count` | int | Number of messages (in list view only) |
| `preview` | string | Last 60 chars of the last message (in list view only) |
| `messages` | Message[] | Full display history (in detail view only) |

## Message roles

| Role | Description |
|------|-------------|
| `user` | Human message (text + optional images/files) |
| `assistant` | Agent response — may be text, tool calls, or both |
| `tool` | Tool execution result (paired with an assistant `tool_calls` entry) |
| `system` | System messages (rare in display history; used internally) |

## Two-buffer design

The session has two internal message lists:

**`messages` (display history)**
- Never modified after writing
- Shown to the user, returned by `GET /sessions/{id}`
- Grows indefinitely

**`context` (LLM context)**
- What the model actually receives on each call
- Compressed automatically when context usage reaches ~80% of the context window
- Compression replaces older messages with a summarized block
- Does not affect `messages`

The `context_compressed` WebSocket event signals that compression ran. You can fetch the current context state from `GET /sessions/{id}/context` (debug endpoint).

## Session lifecycle

1. Create: `POST /sessions` → get `session_id`
2. Connect WebSocket: `ws://host/ws/sessions/{session_id}`
3. Send messages, receive streaming events
4. Sessions persist until explicitly deleted: `DELETE /sessions/{session_id}`

## URL-based navigation

The recommended pattern (used by the original client): store `session_id` in the URL hash (`#session_id`). This allows bookmarking and browser back/forward navigation without server-side routing.

## Pinning

Pinned sessions always appear at the top of `GET /sessions`. Toggle via `PATCH /sessions/{id}/pin`.

## File uploads

Files are uploaded to a session-scoped directory before being referenced in messages:
1. `POST /sessions/{id}/files` → get `{ name, path, size, content_type }`
2. Include the returned object in the `files` array of the next WebSocket message
3. The server appends the file path to the message content so the agent can access it

Files are stored in `session_files/{session_id}/` and auto-deleted 24h after session inactivity.

## Context token tracking

`stream_end` includes `context_tokens` and `max_context_tokens`. Use these to show a context usage indicator if desired. At ~80% usage, compression fires automatically after the response.
