2.`session.AllocateRouteSession` converts the route's `SessionPolicy` plus inbound context into a structured `SessionScope`.
3. The allocator builds:
-`SessionKey`: canonical routed session key
-`SessionAliases`: compatibility aliases for that routed scope
-`MainSessionKey`: agent-level main session key
-`MainAliases`: legacy alias for the main session
4.`runAgentLoop` persists scope metadata and aliases through `ensureSessionMetadata`.
5. During later reads or writes, `JSONLBackend.ResolveSessionKey` maps aliases back onto the canonical key.
The main session key is separate from routed chat sessions.
It is mainly used for agent-level or system-style flows that need one stable per-agent conversation, for example `processSystemMessage`.
## Scope Construction Rules
`pkg/session/allocator.go` builds scope values from normalized inbound context.
Important rules:
-`space` becomes `<space_type>:<space_id>`
-`chat` becomes `<chat_type>:<chat_id>`
-`topic` becomes `topic:<topic_id>`
-`sender` is canonicalized through `session.identity_links` before being stored
There are two special cases worth calling out.
### Telegram forum isolation
Telegram forum topics must stay isolated even when the configured dimensions only mention `chat`.
To preserve that behavior, the allocator appends `/<topic_id>` to the `chat` value for Telegram forum messages unless `topic` is already an explicit dimension.
Example:
```text
group:-1001234567890/42
group:-1001234567890/99
```
Those produce different session keys.
### Identity links
`session.identity_links` lets multiple sender identifiers collapse into one canonical identity.
Both dispatch matching and session allocation use that mapping so that the same person can keep one conversation even if their raw sender IDs differ across channels or accounts.
## Storage Format
The default runtime backend is `pkg/memory.JSONLStore`, wrapped by `session.JSONLBackend`.
Each session uses two files:
```text
{sanitized_key}.jsonl
{sanitized_key}.meta.json
```
The files store:
-`.jsonl`: one `providers.Message` per line, append-only
-`.meta.json`: summary, timestamps, line counts, logical truncation offset, scope, aliases
`SessionMeta` currently includes:
-`Key`
-`Summary`
-`Skip`
-`Count`
-`CreatedAt`
-`UpdatedAt`
-`Scope`
-`Aliases`
## Write And Crash Semantics
The JSONL store is designed around append-first durability and stale-over-loss recovery:
-`AddMessage` and `AddFullMessage` append one JSON line, `fsync`, then update metadata.
-`TruncateHistory` is logical first: it only advances `meta.Skip`.
-`Compact` physically rewrites the JSONL file to remove skipped lines.
-`SetHistory` and `Compact` write metadata before rewriting JSONL so a crash may temporarily expose old data, but should not lose data.
- Corrupt JSONL lines are skipped during reads instead of failing the entire session.