How Munder Difflin Implements Persistent Memory for Agents: Architecture Deep-Dive
Munder Difflin implements persistent memory for agents using plain-text memory.md files stored per-agent, combined with a background MemPalace indexing pipeline for semantic search and automatic condensing to manage file size.
Each agent in the Munder Difflin hive maintains its own long-term knowledge base in a simple markdown file. This design prioritizes durability, inspectability, and portability while adding powerful semantic search capabilities through an optional external CLI tool. The architecture spans agent creation, background indexing, memory maintenance, and runtime query interfaces.
The Core Storage Model: Per-Agent memory.md Files
At the heart of Munder Difflin's persistent memory system lies a single convention: every agent owns a memory.md file located at HIVE_ROOT/agents/<agent-id>/memory.md. This file serves as the source of truth for an agent's accumulated knowledge and survives process restarts, crashes, and shutdowns without requiring a database.
When a new agent is initialized, the hive automatically provisions this file. In [src/main/hive.ts](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts), the ensureAgent() method creates the agent directory structure and writes an initial header to memory.md:
// Simplified excerpt from hive.ts agent provisioning
const agentDir = join(this.root, 'agents', agentId);
await mkdir(agentDir, { recursive: true });
const memoryPath = join(agentDir, 'memory.md');
if (!existsSync(memoryPath)) {
await writeFile(memoryPath, `# ${agentName}\n\n## Memories\n\n`);
}
The plain-text format ensures the memory remains human-readable, version-control friendly, and accessible to standard Unix tools.
The MemPalace Pipeline: From Markdown to Semantic Search
Raw markdown files enable persistence but not efficient search. Munder Difflin solves this through MemPalace, an external CLI tool that builds a searchable embedding index from all agent memories.
Mining: Converting memory.md to Searchable Wings
The MemoryManager class orchestrates the mining pipeline. Located in [src/main/memory.ts](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/memory.ts), it periodically invokes mempalace mine to ingest each agent's memory into a shared palace directory:
// From src/main/memory.ts – the mining operation
async mineNow(): Promise<void> {
for (const agent of this.hive.listAgents()) {
const agentDir = this.hive.agentDir(agent.id);
const wing = agent.id; // Each agent gets its own "wing" in the palace
await execa('mempalace', [
'mine', agentDir,
'--wing', wing,
'--agent', agent.id,
'--output', this.palaceDir
]);
}
}
The manager schedules automatic mining every 10 minutes via setInterval, with additional on-demand mining triggered after significant memory updates.
Controlling the Index: The .gitignore Filter
Not all files in an agent directory belong in the semantic index. The ensureMineIgnore() function in [src/main/memory.ts](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/memory.ts) generates a per-agent .gitignore that excludes:
- Configuration files (
settings.json,cursor.json) - Message queues (
inbox/,outbox/JSON files) - Temporary artefacts
This ensures the MemPalace index contains only meaningful memory content.
Memory Status Tracking
The MemoryStatus interface in [src/main/memory.ts](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/memory.ts) exposes operational state:
interface MemoryStatus {
palaceReady: boolean; // Is MemPalace CLI available?
lastMineTime: Date | null; // When did last mining complete?
indexedAgents: string[]; // Which agents are in the index?
totalEmbeddings: number; // Approximate index size
}
Memory Maintenance: Automatic Condensing with Reflector
Unbounded growth would eventually make memory.md files unwieldy. Munder Difflin addresses this through memory condensing implemented in [src/main/reflect.ts](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/reflect.ts).
The Reflector periodically scans agent memories and restructures oversized files into three regions:
| Region | Purpose |
|---|---|
| Pinned | Critical facts the agent must retain (manually marked) |
| Condensed | LLM-summarized older content |
| Recent | Unprocessed entries from the current session |
The condensing process uses a headless LLM to summarize historical entries while preserving semantic meaning. After condensation, the next mining operation picks up the compacted version, keeping the MemPalace index efficient.
Runtime Access: Querying Persistent Memory
Agents and UI components access memory through two primary interfaces.
The get_memory Tool
The renderer exposes a get_memory tool defined in [src/renderer/src/realtime/tools.ts](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/renderer/src/realtime/tools.ts). This tool implements a dual-path search strategy:
// Simplified from tools.ts
async function get_memory({ query, agentId }: MemoryQuery): Promise<MemoryResult> {
// Path 1: Semantic search via MemPalace CLI (preferred)
if (memoryManager.status.palaceReady) {
const result = await execa('mempalace', [
'search', query,
'--wing', agentId ?? '*',
'--limit', '5'
]);
return parseSemanticResults(result.stdout);
}
// Path 2: Fallback to direct text search across all memory.md files
return grepAllMemories(query);
}
When the MemPalace CLI is available, queries execute as semantic memory searches using vector embeddings. Otherwise, the system falls back to full-text search across all memory.md files.
Direct File Access via IPC
The preload bridge in [src/preload/index.ts](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/preload/index.ts) exposes synchronous memory operations:
| IPC Channel | Purpose |
|---|---|
memoryWakeUp |
Initialize memory subsystem on app launch |
memoryStatus |
Retrieve current MemoryStatus |
hiveMemory |
Read a specific agent's memory.md directly |
// Reading Alice's memory from the renderer process
const aliceMemory: string = await window.cth.hiveMemory('alice');
// Returns raw contents of agents/alice/memory.md
Complete Usage Examples
Creating an Agent with Persistent Memory
import { Hive } from './main/hive';
const hive = new Hive('/path/to/hive');
// ensureAgent creates memory.md automatically
await hive.ensureAgent({
id: 'research-assistant-01',
name: 'Claude',
role: 'Research Assistant'
});
// File now exists at: /path/to/hive/agents/research-assistant-01/memory.md
Appending Knowledge Durably
import { writeFile } from 'fs/promises';
import { join } from 'path';
const memoryPath = join(hive.root, 'agents', agentId, 'memory.md');
// Append with timestamp for chronological organization
const entry = `## ${new Date().toISOString().split('T')[0]} — Key Finding
Discovered that semantic search outperforms keyword matching by 34%
on long-form memory retrieval tasks.\n\n`;
await writeFile(memoryPath, entry, { flag: 'a' }); // 'a' = append
Querying Across All Agent Memories
// From renderer/UI context
const results = await window.cth.get_memory({
query: 'semantic search performance metrics',
limit: 3
});
// Returns:
// {
// source: 'mempalace' | 'grep',
// matches: [
// { agent: 'research-assistant-01', excerpt: '...', score: 0.89 },
// ...
// ]
// }
Checking System Health
const status: MemoryStatus = await window.cth.memoryStatus();
if (!status.palaceReady) {
console.warn('MemPalace CLI unavailable — falling back to text search');
}
console.log(`Last mining: ${status.lastMineTime?.toLocaleString()}`);
console.log(`${status.totalEmbeddings} embeddings across ${status.indexedAgents.length} agents`);
Architecture Comparison: Design Trade-offs
Munder Difflin's persistent memory design makes intentional trade-offs:
- Plain text over database: Maximum portability, universal tool compatibility, trivial backup/restore
- External CLI over embedded vectors: Keeps core runtime lightweight; MemPalace can run on GPU-enabled hosts separately
- Append-only with periodic condensation: Balances write performance against read efficiency; condensing amortizes cleanup cost
- Per-agent files over central store: Natural isolation, easy per-agent export/import, clear ownership boundaries
Summary
- Storage: Each agent's knowledge persists in
HIVE_ROOT/agents/<id>/memory.mdas human-readable markdown - Indexing: The
MemoryManagermines these files into a MemPalace semantic index located in<hive>/palace - Maintenance: The
Reflectorcondenses oversized memories to preserve retrieval performance - Access: The
get_memorytool provides semantic search with text fallback;hiveMemoryenables direct file reads - IPC: All memory operations bridge through [
src/preload/index.ts](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/preload/index.ts) for secure renderer-to-main communication
Frequently Asked Questions
What happens if the MemPalace CLI is not installed?
Munder Difflin degrades gracefully to full-text search. The get_memory tool detects CLI unavailability via MemoryStatus.palaceReady and executes a grep-style search across all memory.md files instead. Results lack semantic ranking but remain functionally correct.
How does memory condensing preserve important information?
The Reflector implements a three-region structure: pinned facts (explicitly preserved), condensed summaries (LLM-compressed history), and recent entries (untouched current session). Users can mark critical memories as pinned to exempt them from summarization. The condensing LLM is instructed to preserve entity relationships, factual claims, and action outcomes even when compressing narrative detail.
Can multiple hives share a single MemPalace index?
No — the palace directory is hive-scoped. Each hive instance maintains its own <hive-home>/palace to prevent cross-contamination between distinct deployments. However, agents can be migrated between hives by copying their memory.md files; the next mining operation in the destination hive will incorporate the transferred memories.
Where is the memory file format documented?
The memory.md schema follows convention over configuration. Headers use standard markdown (# Agent Name, ## Section Title), chronological entries include ISO date prefixes, and pinned items use a <!-- PINNED --> HTML comment marker recognized by the condenser. The ensureAgent() implementation in [src/main/hive.ts](https://github.com/chaitanyagiri/munder-difflin/blob/main/src/main/hive.ts) establishes the initial structure for new agents.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →