Compaction, Memory, and Dream
Corresponding source
| Mechanism | Source |
|---|---|
| Memory files and archive | nanobot/agent/memory.py::MemoryStore |
| Session consolidation | nanobot/agent/memory.py::Consolidator |
| Automatic compaction scheduling | nanobot/agent/autocompact.py::AutoCompact |
| Memory injection into model context | nanobot/agent/context.py::ContextBuilder.build_system_prompt |
| Dream prompt and Git snapshots | nanobot/templates/agent/dream.md, nanobot/utils/gitstore.py |
Learning objectives
After this lesson, you should be able to:
- distinguish four forms of “compression”;
- explain how Session content enters
history.jsonl; - explain how Dream updates long-term files;
- describe the purpose of each cursor;
- identify which Dream configuration fields work and which are deprecated.
Prerequisites
- Understand Session and context windows.
- Recommended: Trimming, summarization, compression, and caching.
1. Separate four mechanisms first
| Mechanism | Trigger | Target | Result |
|---|---|---|---|
| Session replay trimming | every BUILD | history for this Turn | replay fewer messages |
| Runner context governance | every iteration | messages_for_model | temporarily compress/trim tool results and history |
| Consolidator | token pressure or idle compaction | an older safe Session segment | append a summary to history.jsonl |
| Dream | Gateway Cron or /dream | new history + long-term files | update SOUL/USER/MEMORY |
Calling all four “Memory compression” hides their different boundaries.
2. Files managed by MemoryStore
Default workspace:
workspace/
├── SOUL.md
├── USER.md
└── memory/
├── MEMORY.md
├── history.jsonl
├── .cursor
├── .dream_cursor
└── .git/Roles:
| File | Role |
|---|---|
SOUL.md | long-term Agent tone, identity, and communication style |
USER.md | stable user information and preferences |
MEMORY.md | project facts, decisions, and long-term context |
history.jsonl | structured historical material produced by Consolidator |
.cursor | cursor for history writes |
.dream_cursor | last position consumed by Dream |
.git | change history for long-term memory files |
3. history.jsonl is source material, not final long-term Memory
A record resembles:
{
"cursor": 42,
"timestamp": "2026-07-24 10:00",
"content": "- The user selected a particular approach",
"session_key": "cli:direct"
}Verify optional fields in the current source.
append_history():
- serializes cursor allocation and append with a thread lock;
- removes leaked thinking content;
- bounds each record’s content;
- updates
.cursor.
compact_history() and rewrite paths use atomic replacement. Ordinary append and complete rewrite use different persistence strategies, so do not claim that every Memory write uses both fsync and rename.
4. Consolidator
Consolidator.maybe_consolidate_by_tokens():
- acquires the Session’s consolidation lock;
- refreshes the Session reference;
- estimates tokens for the full prompt;
- handles replay-message overflow first;
- compares usage with the input budget;
- chooses a legal consolidation boundary;
- asks the LLM to summarize the older segment;
- appends the summary to
history.jsonl; - advances
last_consolidated; - saves
_last_summary.
flowchart TD S["Session.messages"] --> EST["Estimate prompt tokens"] EST -->|Below budget| KEEP["Do not archive"] EST -->|At budget| B["Choose safe boundary"] B --> LLM["LLM summary"] LLM -->|Success| H["Summary in history.jsonl"] LLM -->|Failure| RAW["Raw archive breadcrumb"] H --> CUR["Advance last_consolidated"] RAW --> CUR CUR --> META["Save _last_summary"]
If LLM summarization fails:
- a bounded raw-archive breadcrumb remains;
- the boundary still advances so the same segment is not archived repeatedly;
- the current operation stops instead of continuing to hammer a degraded Provider.
5. Token budgets
Consolidator’s input budget reserves:
- maximum completion tokens;
- a safety buffer.
consolidation_ratio controls the target proportion after compression, not “how many messages to delete each time.”
Use the current AgentDefaults.consolidation_ratio as authority; it is currently 0.5.
6. AutoCompact
AutoCompact handles idle Sessions:
- the serialized configuration name is
idleCompactAfterMinutes; - the current default is 15 minutes;
0disables it;- AgentLoop checks stale Sessions when inbound queue consumption times out;
- an active Session must not compact concurrently;
- Consolidator’s lock protects hard trimming;
- the summary is stored both in memory and Session metadata so the cold path works after restart.
The next prepare_session() injects _last_summary into the system prompt as Archived Context Summary.
7. Dream
Dream is a system Cron job registered by Gateway and can also be triggered with /dream.
flowchart TD H["New history.jsonl records"] --> BP["MemoryStore.build_dream_prompt"] BP --> DS["Independent Dream Session / Agent run"] DS --> TOOLS["Dream-specific file-editing Tools"] TOOLS --> FILES["SOUL.md / USER.md / MEMORY.md"] FILES --> GIT["GitStore records changes"] DS --> CUR["Advance .dream_cursor"]
Dream reads:
- unconsumed history;
- current SOUL;
- current USER;
- current MEMORY;
- runtime information such as the Skill creator path.
It does not copy all of history.jsonl into MEMORY.md. It uses Tools to make focused edits to long-term files.
8. Dream configuration boundary
Current fields in AgentDefaults.dream:
| Field | Current behavior |
|---|---|
enabled | whether to register the periodic Dream job |
intervalH | default interval |
cron | compatible Cron override |
modelOverride | not implemented |
maxBatchSize | deprecated and unused |
maxIterations | deprecated and unused |
annotateLineAges | deprecated and unused |
A field remaining in the schema is not proof that the feature currently works.
9. How Memory enters the next model request
ContextBuilder may add:
- customized
MEMORY.md; - recent history after
.dream_cursor; - the Session’s
_last_summary; - SOUL/USER as bootstrap files.
This explains why a Memory update does not “overwrite the current Session immediately”:
- the current conversation still depends mainly on Session history;
- Consolidator first produces history;
- Dream periodically organizes long-term files;
- a later ContextBuilder run reads them again.
Observation task
- Locate the memory directory in the learning workspace.
- Complete several conversation rounds.
- Check whether the Session and
history.jsonlchanged. - Compare SOUL/USER/MEMORY before and after
/dream. - Use
/dream-logto inspect GitStore changes. - Do not put real personal information into the experiment.
Source and tests
nanobot/agent/memory.pynanobot/agent/autocompact.pynanobot/agent/context.pynanobot/templates/agent/dream.mdnanobot/utils/gitstore.pytests/agent/test_memory_store.pytests/agent/test_consolidator.pytests/agent/test_consolidation_ratio.pytests/agent/test_auto_compact.pytests/agent/test_dream.pytests/agent/test_dream_session.pytests/agent/test_git_store.py
Self-check
- Why is Session replay trimming not Consolidation?
- Why is
history.jsonlnot final Memory? - Why does Consolidator advance its boundary after a summarization failure?
- What do
.cursorand.dream_cursortrack separately? - Why must
modelOverridebe marked as not implemented?
Next step
Read Goals and Continuations.