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 (default 1500), DiscoClaw runs one extra recompression pass before saving.
  • The recompression target is floor(DISCOCLAW_SUMMARY_MAX_TOKENS * DISCOCLAW_SUMMARY_TARGET_RATIO) (default ratio 0.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:

  • hitCount and lastHitAt are 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.

CommandWhat it does
!memory showLists all durable items + rolling summary
!memory remember <text>Adds a fact to durable memory
!memory forget <substring>Deprecates matching durable items
!memory reset rollingClears 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 remember needed — 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.

LayerDefault budgetDefault state
Durable memory2000 charson
Rolling summary2000 charson
Message history3000 charson
Short-term memory1000 charson
Cold storage1500 charsoff (requires DISCOCLAW_COLD_STORAGE_ENABLED)
Open tasks600 charson
Auto-extractionn/a (write-side only)on
Workspace filesno budgeton (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 prompt
  • DISCOCLAW_SUMMARY_MAX_CHARS — longer rolling summaries
  • DISCOCLAW_MESSAGE_HISTORY_BUDGET — more message history
  • DISCOCLAW_SHORTTERM_INJECT_MAX_CHARS — more cross-channel context

Tune rolling-summary token recompression:

  • DISCOCLAW_SUMMARY_MAX_TOKENS — estimated-token threshold that triggers one-pass recompression
  • DISCOCLAW_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 facts
  • DISCOCLAW_SUMMARY_ENABLED=false — no rolling summaries
  • DISCOCLAW_SHORTTERM_MEMORY_ENABLED=false — no cross-channel awareness
  • DISCOCLAW_SUMMARY_TO_DURABLE_ENABLED=false — no auto-extraction
  • DISCOCLAW_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

VariableDefaultDescription
DISCOCLAW_MESSAGE_HISTORY_BUDGET3000Character budget for message history
DISCOCLAW_SUMMARY_ENABLEDtrueEnable rolling summaries
DISCOCLAW_SUMMARY_MODELfastModel tier for summary generation
DISCOCLAW_SUMMARY_MAX_CHARS2000Max chars for rolling summary
DISCOCLAW_SUMMARY_EVERY_N_TURNS5Turns between summary updates
DISCOCLAW_SUMMARY_MAX_TOKENS1500Estimated-token threshold that triggers one-pass rolling-summary recompression
DISCOCLAW_SUMMARY_TARGET_RATIO0.65Recompression target ratio; target tokens are floor(maxTokens * ratio)
DISCOCLAW_DURABLE_MEMORY_ENABLEDtrueEnable durable memory
DISCOCLAW_DURABLE_INJECT_MAX_CHARS2000Max chars injected per prompt
DISCOCLAW_DURABLE_MAX_ITEMS200Max items per user
DISCOCLAW_MEMORY_COMMANDS_ENABLEDtrueEnable !memory commands
DISCOCLAW_SUMMARY_TO_DURABLE_ENABLEDtrueEnable auto-extraction
DISCOCLAW_DURABLE_SUPERSESSION_SHADOWfalseShadow mode for supersession
DISCOCLAW_MEMORY_CONSOLIDATION_THRESHOLD50Legacy consolidation knob (currently not wired to runtime compaction path)
DISCOCLAW_MEMORY_CONSOLIDATION_MODELfastLegacy consolidation knob (currently not wired to runtime compaction path)
DISCOCLAW_SHORTTERM_MEMORY_ENABLEDtrueEnable short-term cross-channel memory
DISCOCLAW_SHORTTERM_MAX_ENTRIES20Max short-term entries
DISCOCLAW_SHORTTERM_MAX_AGE_HOURS6Expiry for short-term entries
DISCOCLAW_SHORTTERM_INJECT_MAX_CHARS1000Max chars for short-term injection
DISCOCLAW_COLD_STORAGE_ENABLEDfalseEnable cold-storage subsystem
DISCOCLAW_COLD_STORAGE_INJECT_MAX_CHARS1500Max chars for cold-storage prompt section
DISCOCLAW_COLD_STORAGE_SEARCH_LIMIT10Max 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_ITEMS to 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_TURNS to extract less frequently.

Short-term memory creating confusion:

  • Disable with DISCOCLAW_SHORTTERM_MEMORY_ENABLED=false.
  • Or reduce DISCOCLAW_SHORTTERM_MAX_AGE_HOURS for shorter context windows.