The xllm-session governance layer
The context window is a scarce resource. xllm remembers nothing — xllm-session remembers for you: budgets, a turn ledger, compaction and recovery, so multi-turn conversations survive a world with hot reload.
Overview
build.bat xllm-session variant (xllm comes along automatically).35.1 Position and model
Session governance answers three questions: what to remember (the turn ledger — the full user/assistant/tool sequence), when to economize (the context budget and compaction pressure lines), and how to survive (journal persistence and recovery after a process restart). It depends only on xllm's public API — it executes no tools and places no calls: driving calls remains your code's job.
Relation to ch. 28 state handover: a session handle exported by ServiceSwap is reclaimed in the new generation's Init, so hot reload never cuts a conversation; the journal covers process-level restarts.
35.2 Context budget
Every session has a window budget: uContextWindowTokens minus the output reserve and the safety reserve is the usable input allowance. Token counting uses xllm's offline estimator; three pressure lines drive policy:
| Pressure | Threshold | Action |
|---|---|---|
| Green | < 75% | Append turns normally |
| Yellow | ≥ 75% | Prepare compaction: mark summary candidates, archive old turns |
| Red | ≥ 95% | Forced compaction transaction: replace old turns with a summary, free allowance |
Budget numbers combine the model profile (ch. 34) with config overrides; helpers like xllmSessionComputeSafetyReserve provide recommended values.
35.3 Turns and the ledger
The session object is the ledger: xllmSessionCreate builds one, xllmSessionAddText / AddAssistantResponse / AddToolResult append turns, and xllmSessionBuildRequest produces an xllm-ready request in one step (auto-trimmed to budget). Fork deep-copies a branch — experiments and rollbacks never pollute the main line.
35.4 Compaction and journal
Compaction is a transaction: Prepare produces a summary and a retention plan, xllmCompactionEvaluateSummary applies a deterministic quality gate (so summaries can't silently drop key constraints), and Commit atomically replaces the ledger. The journal is an NDJSON append stream: every turn hits disk, checkpoints snapshot periodically, and xllmSessionRecover replays it — conversations continue after a crash.
35.5 The contract
| Object | Ownership | Release |
|---|---|---|
xllm_session_config | caller's stack value | none |
xllm_session | library-allocated | xllmSessionDestroy |
xllm_compaction | compaction transaction | xllmCompactionDestroy |
| journal file | library-managed | finalized with the session |
Threading: session objects are not thread-safe — one thread per session (bind to a fixed worker or background thread, the discipline of ch. 41); crossing hot reload goes through Swap/Take. Scripts probe with #ifdef XS_USE_XLLM_SESSION; nested TCC sees the same.
Common mistakes
- Symptom
- Long conversations get interrupted by forced compaction at the 95% line.
- Cause
- The window must minus the output and safety reserves (the profile's recommended values); the budget helpers already do this — hand math misses it.
- Symptom
- Under concurrent requests the ledger corrupts and compaction transactions interleave.
- Cause
- Session objects carry no internal lock — the ledger is a single-writer model. Bind each session to one thread, or hold an application-side mutex.
Exercises
Create a session, append five text turns, BuildRequest and reconcile token counts with estimates.
Load enough history to cross the 75% line; run Prepare/Evaluate/Commit and compare request sizes.
Enable the journal, converse, kill the process, restart and Recover to continue.
Summary
| Point | Conclusion |
|---|---|
| Position | governance above xllm: ledger / budget / compaction / recovery |
| Budget | window minus output and safety reserves; 75%/95% pressure lines |
| Compaction | Prepare → quality gate → Commit, transactional replacement |
| Persistence | NDJSON journal + checkpoints + Recover |
| Concurrency | one thread per session; cross-reload via Swap/Take |
- xrt · json — the NDJSON under the journal