An educational local C# project showing how long-running AI agent sessions can stay bounded by combining pinned facts, rolling summaries, expiry rules, conflict resolution, deterministic context budgeting, and an optional local Ollama response path.
This version uses a coding agent scenario with a multi-turn checkout bug-fix session, carry-forward state snapshots, an optional Ollama-generated next-turn draft, and file-backed JSON reports.
This repository demonstrates a practical session-memory control flow:
- Load runtime configuration for dataset path, report directory, token budget, recent-turn window, summary depth, and fact TTLs.
- Replay a frozen multi-turn coding-agent session from JSON.
- Normalize fact writes before storage, including trimming, max-length checks, and secret redaction.
- Keep pinned constraints and goals durable across turns.
- Expire short-lived or stale session facts before the next carry-forward snapshot.
- Resolve conflicting fact writes deterministically so newer trusted facts replace weaker ones.
- Compact older interaction history into a rolling summary while preserving the most recent turns.
- Pack the next-turn context under an explicit token budget and exclude blocks that should not survive.
- Optionally send only the packed carry-forward context to a local Ollama model for the next-turn draft.
- Persist the full turn-by-turn report as JSON for later inspection.
The result is a real context-engineering control layer without pretending that raw chat history should be passed forward indefinitely.
- Deterministic session memory for long-running AI agent conversations
- Pinned facts for goals, constraints, and verification commands
- Rolling summaries that compress older turns once the recent-turn window is exceeded
- TTL-based expiry for short-lived session notes
- Conflict resolution where newer trusted facts override earlier values
- Secret redaction before memory persistence
- Explicit carry-forward budgeting with included and excluded context blocks
- Optional Ollama-backed next-turn drafting over deterministic carry-forward state
- File-backed JSON reports showing every turn snapshot and final prompt preview
- .NET 10 SDK or later
- Optional for live model drafting:
- Ollama running locally at
http://localhost:11434 - a pulled chat-capable model such as
qwen3:8b
- Ollama running locally at
The deterministic compaction pipeline always runs locally against the frozen session dataset. The model stage is optional and uses only the packed carry-forward context.
From the project root:
ollama serve
dotnet test SessionMemoryContextCompaction.slnx
dotnet run --project SessionMemoryContextCompactionThe app prints:
- active, expired, superseded, and rejected fact counts per turn
- token usage for every carry-forward snapshot
- the final active verification command after conflict resolution
- an optional Ollama-generated next-turn draft or a model error if Ollama is unavailable
- the saved JSON report path
Default settings are in SessionMemoryContextCompaction/appsettings.json under App.
Example:
{
"App": {
"DatasetPath": "data/coding_agent_session.json",
"ReportDirectory": "data/reports",
"ModelContextWindowTokens": 2200,
"ReservedOutputTokens": 450,
"FixedPromptTokens": 240,
"MaxRecentTurns": 2,
"MaxSummaryEntries": 4,
"DefaultFactTtlMinutes": 180,
"ShortLivedFactTtlMinutes": 30,
"MaxFactValueChars": 280,
"EnableModelCall": true,
"OllamaBaseUrl": "http://localhost:11434",
"OllamaModelId": "qwen3:8b",
"OllamaTimeoutSeconds": 45,
"OllamaTemperature": 0.0,
"OllamaMaxOutputTokens": 220
}
}Environment variable overrides use prefix CTXMEM_:
CTXMEM_App__DatasetPathCTXMEM_App__ReportDirectoryCTXMEM_App__ModelContextWindowTokensCTXMEM_App__ReservedOutputTokensCTXMEM_App__FixedPromptTokensCTXMEM_App__MaxRecentTurnsCTXMEM_App__MaxSummaryEntriesCTXMEM_App__DefaultFactTtlMinutesCTXMEM_App__ShortLivedFactTtlMinutesCTXMEM_App__MaxFactValueCharsCTXMEM_App__EnableModelCallCTXMEM_App__OllamaBaseUrlCTXMEM_App__OllamaModelIdCTXMEM_App__OllamaTimeoutSecondsCTXMEM_App__OllamaTemperatureCTXMEM_App__OllamaMaxOutputTokens
Disable live model drafting:
set CTXMEM_App__EnableModelCall=false
dotnet run --project SessionMemoryContextCompactionProgram.csloads config, replays the frozen session, optionally invokes Ollama with the packed final context, saves the report, and prints the final carry-forward summary.SessionDatasetLoaderloads the multi-turn session dataset from JSON.MemoryWritePolicyvalidates fact writes, assigns TTLs, and redacts secrets before anything is stored.ContextCompactionEngineexpires stale facts, applies deterministic conflict resolution, builds rolling summaries, and packs the next-turn context under budget.NextTurnPromptComposerturns the final carry-forward snapshot into a bounded model prompt.OllamaDraftClientcalls the local Ollama/api/generateendpoint and strips<think>traces from the returned draft.JsonReportStorepersists the turn-by-turn compaction report for later replay and inspection.
.
+-- SessionMemoryContextCompaction.slnx
+-- SessionMemoryContextCompaction/
| +-- SessionMemoryContextCompaction.csproj
| +-- Program.cs
| +-- appsettings.json
| +-- App/
| | +-- AppConfig.cs
| +-- Domain/
| | +-- CompactionReport.cs
| | +-- MemoryFact.cs
| | +-- SessionDataset.cs
| +-- Persistence/
| | +-- JsonReportStore.cs
| +-- Services/
| +-- ContextCompactionEngine.cs
| +-- HeuristicTokenEstimator.cs
| +-- MemoryWritePolicy.cs
| +-- NextTurnPromptComposer.cs
| +-- OllamaDraftClient.cs
| +-- SecretRedactor.cs
| +-- SessionDatasetLoader.cs
| +-- data/
| +-- coding_agent_session.json
+-- SessionMemoryContextCompaction.Tests/
| +-- SessionMemoryContextCompaction.Tests.csproj
| +-- ContextCompactionEngineTests.cs
| +-- SecretRedactorTests.cs
+-- .gitignore
+-- LICENSE
+-- README.md
- Pinned task constraints survive across turns unless a newer trusted source intentionally changes them
- Short-lived notes expire before they silently distort later model calls
- Older history is compacted into a bounded rolling summary instead of being replayed in full
- Redacted secrets are not carried into the next-turn context
- The carry-forward state is built under an explicit token budget, with exclusions recorded by reason
- The live model sees only the packed carry-forward context, never the raw full transcript
Run the test project from the repo root:
dotnet test SessionMemoryContextCompaction.slnxThe current test suite covers:
- pinned fact survival across turns
- expiry of short-lived facts
- deterministic override of an updated verification command
- rolling-summary creation once the recent-turn window is exceeded
- redacted secret exclusion from carry-forward context
- next-turn prompt composition
- cleanup of
<think>traces from model output
See the LICENSE file for details.
Contributions are welcome for improvements within current project scope.
Suggested areas:
- add more session scenarios such as support operations or incident coordination
- introduce per-scope TTL policies and stronger conflict rules
- calibrate token estimation against real model tokenizers
- extend the report format with longitudinal snapshot comparisons