# Sessions

Sessions store the conversation history between users and the agent. Each channel conversation gets its own isolated session.

## Session Keys

Messages from a chat channel (Discord, Teams, Telegram, Facebook) get the key `{channel}:{peerID}`. With more than one agent in `agents.list`, the key starts with the agent the message was routed to: `{agentID}:{channel}:{peerID}`.

| Channel | Example Key | Scope |
|---------|-------------|-------|
| Discord DM | `discord:dm:987654321098765432` | Per user |
| Discord Guild | `discord:123456789012345678:987654321098765432` | Per user in each guild channel (channel ID, then user ID) |
| MS Teams | `msteams:19:0123456789abcdef0123456789abcdef@thread.v2` | Per conversation: one session for everyone in a group chat or channel, one per user in a 1:1 chat |
| Telegram | `telegram:123456789` | Per chat |
| Multi-agent | `agent-ops:msteams:19:0123456789abcdef0123456789abcdef@thread.v2` | As above, per agent |

When `session.identity_links` lists a person's accounts on several channels, all of them use the key of the first account in the list, so they share one session. Each account is written as the channel and the peer ID of its key in the table above, for example `telegram:123456789` or `discord:dm:987654321098765432` (a Discord DM); upper and lower case are not distinguished.

```yaml
session:
  identity_links:
    alice: ["telegram:123456789", "discord:dm:987654321098765432"]
```

Other entry points name their sessions themselves:

| Entry point | Key | Scope |
|-------------|-----|-------|
| Web UI chat (`POST /api/chat`) | the request's `session_id`: `admin-session`, or an `admin-…` conversation the page created | Per named conversation, shared by the browser's tabs |
| WebSocket on channel `web` | `admin-conn-<connection ID>`, or an `admin-…` session ID the client sends | Per connection, or per named conversation |
| WebSocket from other clients | the session ID the client sends, else `p2p:<peer>:<default agent>` | Per peer |
| `/api/webhook` (and `goclaw message`) | `p2p:<peer>:<default agent>`; channel `web` with the gateway token uses `admin-session`; with `subject_ref` (a delegated cron `agent …` job), `p2p:<peer>:<default agent>:for:<subject>` | Per peer, and per person a job acts for |
| OpenAI-compatible API | `openai-compat:<client IP>` | Per client IP |
| App API | `app_api:<user>`, or `app_api:<user>/<session_id>` (both URL-escaped) | Per employee, and per session the app asks for |
| Webhook (chat) | `webhook:<agent>:<type>:<entity>` | Per object the event is about (merge request, issue, alert); `ts-<time>` when the payload names none |
| Subagent run | `agent:<parent agent>:subagent:job-<time>` | Per run |

## Storage Backends

### SQLite (GORM)

Persistent storage using the main GoClaw SQLite database. Sessions survive gateway restarts and support transactional updates.

### In-Memory

Non-persistent storage for development and testing. All sessions are lost on restart.

Used automatically when the database is unavailable.

## Session Lifecycle

1. **Creation**: A new session is created on the first message from a peer
2. **Retrieval**: Session history is loaded and passed to the agent on each turn
3. **Updates**: Each user message and agent response is appended to the history. A session is written by one turn at a time: a second message to the same session waits until the first is answered (see [One Turn per Session](../event_driven_actor.md#one-turn-per-session))
4. **Reset**: `/new` or `/reset` (`session.reset_triggers`) clears the session. `session.reset` (`mode: daily` with `at_hour`, or `mode: idle` with `idle_minutes`) and `session.reset_by_type` (per channel) clear sessions automatically; they are off by default
5. **Cleanup**: Sessions are not deleted by age. They stay until they are reset. The exception is webhook sessions made for a single event (`webhook:<agent>:<type>:ts-…`, and the SOP-run logs `webhook-sop:…:ts-…` and `webhook-sop:…:batch-…`), removed after 7 days without activity. There is no other pruning: `session.maintenance`, which was never read, has been removed, and a config that still has it gets a warning at startup.

## History Format

Session history uses LangChainGo's `MessageContent` format:

```go
type Session struct {
    ID       string
    History  []llms.MessageContent
    LastSeen time.Time
}
```

Each history entry is either:
- `ChatMessageTypeHuman` — user messages
- `ChatMessageTypeAI` — agent responses: the reply's text, then a `tool_call` part for each tool the
  turn called (up to 64), with its name and its arguments. Each string in the arguments is cut to 80
  characters and the whole to 200, and a key that names a credential (`password`, `api_key`,
  `Authorization`, `…token`) is left out. A failed call's part has the ID `failed`.

Tool results stay inside their turn and are never stored. On a later turn the model reads the earlier
calls as one line at the start of the reply they belong to, for example
`[Tool calls behind this reply, recorded by GoClaw; results not kept: datetime {"timezone":"UTC"} ×2]`,
so it knows what it has already done. Without that line, some models said they had used no tools and
ran the same calls again. The line costs about 15–40 tokens for each earlier reply that used tools
(eight distinct calls at most, then a count). The Sessions page and the chat show the reply's text
only.

## Session Inspection

### Via REST API

Both endpoints need the gateway token:

```bash
# List all sessions
curl -H "X-Goclaw-Token: $GOCLAW_TOKEN" http://localhost:8080/api/sessions

# Inspect sessions with memory
curl -H "X-Goclaw-Token: $GOCLAW_TOKEN" http://localhost:8080/api/inspect
```

### Via Agent Tools

In a human admin's conversation, the agent has the `sessions_list` and `sessions_history`
tools. They read any session of the agent, other people's included, so no one else is
offered them:

```
User: Show me all active sessions

Agent: [uses sessions_list tool]
Active sessions:
- discord:123456789012345678:987654321098765432 (12 messages, last seen 5 min ago)
- msteams:19:0123456789abcdef0123456789abcdef@thread.v2 (3 messages, last seen 1 hour ago)
```

### Via Web UI

The Web UI Dashboard shows active sessions with message counts and last activity timestamps.

## Session Isolation

- Each session key has its own history
- Sessions do not share history across channels or peers, except accounts joined by `session.identity_links`
- Long-term memory (`Memory.md`) is shared across all of an agent's sessions via its workspace
- Skills are injected into every session's system prompt
