DiscoClaw Memory System
March 30, 2026 · View on GitHub
DiscoClaw's memory system gives your assistant persistent context across conversations, channels, and restarts. It combines seven runtime layers so the bot remembers what you told it, what you were discussing, and what's happening across your server.
Memory Layers
1. Rolling Summaries — conversation continuity
Compresses conversation history into a running summary, updated every N turns (default 5). Keyed per session (user + channel pair). Automatic and invisible.
What you see:
- The bot "remembers" what you were discussing, even after a gap.
- After restarting, it still knows you were debugging a CI pipeline or planning a trip.
User (turn 1): Hey, I'm working on migrating our API from Express to Fastify
Bot: Nice — what version of Fastify? Any middleware you need to port?
User (turn 6): What were we talking about?
Bot: We've been working through your Express → Fastify migration.
You've ported the auth middleware and are stuck on the
request validation layer.
Rolling Summary Token Recompression
After each summary refresh, DiscoClaw estimates summary size as ceil(chars / 4) tokens.
- If the estimate exceeds
DISCOCLAW_SUMMARY_MAX_TOKENS(default1500), DiscoClaw runs one extra recompression pass before saving. - The recompression target is
floor(DISCOCLAW_SUMMARY_MAX_TOKENS * DISCOCLAW_SUMMARY_TARGET_RATIO)(default ratio0.65). - Recompression is one-pass only (no retry loop). If the result still exceeds threshold, DiscoClaw logs a warning and continues.
Logging visibility:
- Recompression logs include before/after estimated tokens, threshold, and target.
- A warning is emitted when the one-pass recompressed summary remains above threshold.
Summary safety behavior is unchanged:
- If recompression fails or returns empty content, DiscoClaw keeps the pre-recompression summary.
- The saved summary is still capped by
DISCOCLAW_SUMMARY_MAX_CHARS.
2. Durable Memory — long-term user facts
A structured store of user facts that persists across all conversations and restarts. Each item has a kind (fact, preference, project, constraint, person, tool, workflow), deduplication by content hash, and a 200-item cap per user. Injection is hot-tier bounded to keep prompts lean: active durable memory is auto-compacted to about 25 items or ~2000 chars.
What you see:
- The bot knows your preferences, projects, and key facts across all channels.
- Works everywhere, not just where the fact was stored.
- Survives restarts, deploys, and long gaps.
User: !memory remember I prefer Rust over Go for systems work
Bot: Remembered: "I prefer Rust over Go for systems work"
(days later, different channel)
User: Should I write this CLI tool in Go or Rust?
Bot: Given your preference for Rust in systems work, I'd lean that way —
especially since this is a low-level networking tool.
Hot-Tier Compaction
Durable memory compacts itself automatically when the active set grows beyond the thread target (25 items or ~2000 chars). Compaction demotes the lowest-value active items first.
Demotion signal:
hitCountandlastHitAtare the decay signal. Items with no hits are demoted first, then lower/older hit patterns are demoted before frequently/recently-hit items.- Demoted items stay in durable storage as
deprecated(history is preserved), but only active items are injected.
3. Memory Commands — user-facing control
Manual interface for reading and writing memory. These commands are intercepted before the AI runtime.
| Command | What it does |
|---|---|
!memory show | Lists all durable items + rolling summary |
!memory remember <text> | Adds a fact to durable memory |
!memory forget <substring> | Deprecates matching durable items |
!memory reset rolling | Clears rolling summary for current session |
User: !memory show
Bot: Durable memory (3 items):
- [fact] Works at Acme Corp (src: manual)
- [preference] Prefers Rust over Go for systems work (src: manual)
- [project] Building a Discord bot called DiscoClaw (src: summary)
Rolling summary:
User discussed adding webhook support to their Fastify migration...
User: !memory forget Acme
Bot: Deprecated 1 item matching "Acme"
4. Auto-Extraction — passive fact collection
After summary refreshes, a separate fast-tier call extracts up to 3 notable facts from the user's message and writes them to durable memory. Enabled by default.
What you see:
- The bot passively picks up things you mention without being asked.
- No
!memory rememberneeded — facts accumulate naturally. - Only extracts what the user explicitly stated, not inferences.
User: I just switched teams — I'm on the platform team now, working with
Kubernetes and Terraform mostly.
Bot: Cool, platform work! What's your first project?
(behind the scenes, auto-extracted to durable memory:)
[fact] On the platform team
[tool] Works with Kubernetes and Terraform
Supersession: When a new fact contradicts an older one (e.g., "I switched to Neovim" supersedes "I prefer Vim"), the old item is automatically deprecated.
5. Short-Term Memory — cross-channel awareness
Records brief summaries of recent exchanges across public guild channels. Entries expire after 6 hours (configurable). Creates continuity across the server.
What you see:
- Switching from #dev to #general doesn't lose context.
- The bot knows what you were just doing in other channels.
(in #dev)
User: Can you help me debug this failing test? It's the auth middleware one.
Bot: Sure — looks like the mock isn't returning the right token format...
(switch to #general, 10 minutes later)
User: Hey, quick question about JWT expiry
Bot: Sure — is this related to the auth middleware test you were debugging
in #dev? The token format issue might be connected to expiry handling.
6. Cold Storage — semantic recall
Searchable archive of past conversations, powered by SQLite + sqlite-vec for vector search, FTS5 for keyword search, and Reciprocal Rank Fusion (RRF) to merge both ranking signals. When a message arrives, cold storage generates an embedding for the user's query and retrieves the most relevant historical chunks. Results are injected into the prompt's primacy zone — high-attention placement so the AI treats retrieved context as foundational background.
Messages are automatically chunked and embedded on ingestion. Retrieval is fail-open: if the embedding API is slow (>3s timeout), the DB is unavailable, or no results match, the prompt simply omits the cold storage section with no error visible to the user.
What you see:
- The bot recalls specific details from conversations that happened days or weeks ago.
- No manual bookmarking — all guild messages are indexed automatically.
- Works across channels (optional channel filter via
COLD_STORAGE_CHANNEL_FILTER).
(two weeks ago, in #dev)
User: We decided to use RRF for merging vector and keyword scores —
it's rank-based so it doesn't need score normalization.
(today, in #general)
User: What approach did we pick for combining search results?
Bot: You went with Reciprocal Rank Fusion (RRF) — it merges vector
and keyword rankings without needing score normalization. That
decision was made in #dev about two weeks ago.
HyDE query rewriting: Before vector search, cold storage generates a hypothetical answer to the user's query using a fast LLM call, then embeds that hypothetical answer instead of the raw query. This bridges the vocabulary gap between short questions and stored content, improving semantic retrieval accuracy. The FTS5 keyword leg still searches against the original raw query, so exact-match recall is unaffected.
Requires an embedding API (OpenAI or any OpenAI-compatible endpoint). Enable with DISCOCLAW_COLD_STORAGE_ENABLED=true. See docs/configuration.md for all cold storage env vars.
7. Workspace Files — human-curated memory
Curated long-term notes (workspace/MEMORY.md) and daily scratch logs (workspace/memory/YYYY-MM-DD.md). Loaded in DMs only. These hold things too nuanced for structured durable items — decisions and personal project context. Reusable engineering lessons belong in docs/compound-lessons.md.
Summary Archive
Each time a rolling summary is refreshed, the outgoing summary (the one being replaced) is appended to a date-partitioned JSONL file:
memory/summary-archive/YYYY-MM-DD.jsonl
Each line is a JSON object containing the session key, channel ID, timestamp, and the full summary text that was overwritten. The archive is append-only — entries are never modified or deleted by the summarizer.
This provides an episodic history of past summaries that can be searched offline (e.g., grep by date, channel, or keyword). No retrieval pipeline reads the archive at runtime yet — it exists purely as a historical record for future use.
Token Budget & Overhead
Each layer has its own character budget. Empty layers are omitted entirely (no header, no separator). The three memory builders run in parallel so they add no latency.
| Layer | Default budget | Default state |
|---|---|---|
| Durable memory | 2000 chars | on |
| Rolling summary | 2000 chars | on |
| Message history | 3000 chars | on |
| Short-term memory | 1000 chars | on |
| Cold storage | 1500 chars | off (requires DISCOCLAW_COLD_STORAGE_ENABLED) |
| Open tasks | 600 chars | on |
| Auto-extraction | n/a (write-side only) | on |
| Workspace files | no budget | on (DMs only) |
With all layers at default settings (cold storage disabled), worst-case memory overhead is ~8600 chars (~2150 tokens). With cold storage enabled, add up to 1500 chars (~375 tokens). In practice most prompts use far less — a user with 5 durable items and a short summary might add ~500 chars total.
How to Tune Memory
Want more memory context? Increase the character budgets:
DISCOCLAW_DURABLE_INJECT_MAX_CHARS— more durable facts per promptDISCOCLAW_SUMMARY_MAX_CHARS— longer rolling summariesDISCOCLAW_MESSAGE_HISTORY_BUDGET— more message historyDISCOCLAW_SHORTTERM_INJECT_MAX_CHARS— more cross-channel context
Tune rolling-summary token recompression:
DISCOCLAW_SUMMARY_MAX_TOKENS— estimated-token threshold that triggers one-pass recompressionDISCOCLAW_SUMMARY_TARGET_RATIO— recompression target ratio relative to that threshold
Tune cold storage retrieval:
DISCOCLAW_COLD_STORAGE_INJECT_MAX_CHARS— max chars injected per prompt (default 1500)DISCOCLAW_COLD_STORAGE_SEARCH_LIMIT— max chunks searched (default 10)COLD_STORAGE_CHANNEL_FILTER— comma-separated channel IDs to restrict ingestion/retrieval
Want less memory overhead? Disable layers you don't need:
DISCOCLAW_DURABLE_MEMORY_ENABLED=false— no long-term factsDISCOCLAW_SUMMARY_ENABLED=false— no rolling summariesDISCOCLAW_SHORTTERM_MEMORY_ENABLED=false— no cross-channel awarenessDISCOCLAW_SUMMARY_TO_DURABLE_ENABLED=false— no auto-extractionDISCOCLAW_COLD_STORAGE_ENABLED=false— no semantic recall (default)
Control auto-extraction aggressiveness:
DISCOCLAW_SUMMARY_EVERY_N_TURNS— how often extraction runs (default 5)DISCOCLAW_DURABLE_MAX_ITEMS— cap per user (default 200)DISCOCLAW_DURABLE_SUPERSESSION_SHADOW=true— observe supersession without acting (shadow mode)
Hot-tier compaction behavior:
- Active durable memory compacts automatically at 25 items or ~2000 chars.
- Demotion priority is driven by
hitCount+lastHitAt(with recency decay).
Configuration Reference
| Variable | Default | Description |
|---|---|---|
DISCOCLAW_MESSAGE_HISTORY_BUDGET | 3000 | Character budget for message history |
DISCOCLAW_SUMMARY_ENABLED | true | Enable rolling summaries |
DISCOCLAW_SUMMARY_MODEL | fast | Model tier for summary generation |
DISCOCLAW_SUMMARY_MAX_CHARS | 2000 | Max chars for rolling summary |
DISCOCLAW_SUMMARY_EVERY_N_TURNS | 5 | Turns between summary updates |
DISCOCLAW_SUMMARY_MAX_TOKENS | 1500 | Estimated-token threshold that triggers one-pass rolling-summary recompression |
DISCOCLAW_SUMMARY_TARGET_RATIO | 0.65 | Recompression target ratio; target tokens are floor(maxTokens * ratio) |
DISCOCLAW_DURABLE_MEMORY_ENABLED | true | Enable durable memory |
DISCOCLAW_DURABLE_INJECT_MAX_CHARS | 2000 | Max chars injected per prompt |
DISCOCLAW_DURABLE_MAX_ITEMS | 200 | Max items per user |
DISCOCLAW_MEMORY_COMMANDS_ENABLED | true | Enable !memory commands |
DISCOCLAW_SUMMARY_TO_DURABLE_ENABLED | true | Enable auto-extraction |
DISCOCLAW_DURABLE_SUPERSESSION_SHADOW | false | Shadow mode for supersession |
DISCOCLAW_MEMORY_CONSOLIDATION_THRESHOLD | 50 | Legacy consolidation knob (currently not wired to runtime compaction path) |
DISCOCLAW_MEMORY_CONSOLIDATION_MODEL | fast | Legacy consolidation knob (currently not wired to runtime compaction path) |
DISCOCLAW_SHORTTERM_MEMORY_ENABLED | true | Enable short-term cross-channel memory |
DISCOCLAW_SHORTTERM_MAX_ENTRIES | 20 | Max short-term entries |
DISCOCLAW_SHORTTERM_MAX_AGE_HOURS | 6 | Expiry for short-term entries |
DISCOCLAW_SHORTTERM_INJECT_MAX_CHARS | 1000 | Max chars for short-term injection |
DISCOCLAW_COLD_STORAGE_ENABLED | false | Enable cold-storage subsystem |
DISCOCLAW_COLD_STORAGE_INJECT_MAX_CHARS | 1500 | Max chars for cold-storage prompt section |
DISCOCLAW_COLD_STORAGE_SEARCH_LIMIT | 10 | Max chunks returned per search |
Troubleshooting
Bot doesn't remember anything across restarts:
- Check that
DISCOCLAW_DURABLE_MEMORY_ENABLED=true(the default). Durable memory is the cross-restart layer. - Rolling summaries reset on restart; durable facts persist.
Too many durable items / memory feels noisy:
- Lower
DISCOCLAW_DURABLE_MAX_ITEMSto cap total items. - Use
!memory forget <substring>to prune specific items. - Hot-tier compaction auto-demotes low-value active items once the active set exceeds 25 items or ~2000 chars.
Auto-extraction picking up irrelevant facts:
- Disable with
DISCOCLAW_SUMMARY_TO_DURABLE_ENABLED=false. - Or increase
DISCOCLAW_SUMMARY_EVERY_N_TURNSto extract less frequently.
Short-term memory creating confusion:
- Disable with
DISCOCLAW_SHORTTERM_MEMORY_ENABLED=false. - Or reduce
DISCOCLAW_SHORTTERM_MAX_AGE_HOURSfor shorter context windows.