Skip to content
Navigation

Epic: 5 — Enhanced Memory System Date: 2026-03-10

This document maps agent-core’s (openJiuwen) 5-type memory system to Exo’s enhanced exo-memory package, covering encryption, deduplication, taxonomy, migration, and unified search.


1. Agent-Core Overview

Agent-core’s memory system lives in openjiuwen/core/memory/ and provides typed, encrypted, deduplicated memory storage for long-running agents.

Key Components

MemoryType (Taxonomy) — An enum classifying what kind of knowledge a memory item represents. Agent-core defines five types:

MemoryTypePurpose
USER_PROFILEUser preferences, traits, and biographical info
SEMANTIC_MEMORYFactual knowledge and learned concepts
EPISODIC_MEMORYEvent-specific memories tied to time and place
VARIABLERuntime variables and ephemeral state
SUMMARYCompressed summaries of conversation or knowledge

AES-256 Encryption at Rest — Agent-core encrypts memory content before persisting it. Uses AES-256 with a password-derived key (PBKDF2). All content fields are encrypted; metadata remains in plaintext for indexing. Keyword search on encrypted content is not supported (ciphertext is opaque).

MemUpdateChecker (LLM Dedup) — Prevents memory bloat by comparing new memories against existing ones using an LLM call. Returns one of four decisions:

DecisionMeaning
ADDNew information is distinct — store it
SKIPDuplicate — same information already exists
MERGEOverlapping — combine into a single memory
DELETEExisting memories are outdated/superseded

Falls back to exact content matching when no LLM is available.

Migration System — Versioned schema migrations for the underlying storage (SQLite or Postgres). Tracks applied versions in a _migrations metadata table with monotonically increasing version numbers.

SearchManager (Unified Search) — Queries multiple memory backends in a single call. Gathers results in parallel, deduplicates by item ID, and returns results sorted by recency.


2. Exo Equivalent

Exo’s memory system lives in the exo-memory package (packages/exo-memory/) and implements the same five-type taxonomy, encryption, deduplication, and migration capabilities — restructured around a MemoryStore protocol for pluggable backends.

Architecture Difference

Where agent-core uses a monolithic memory manager, Exo separates concerns into composable components behind a protocol interface:

python
# Agent-core: monolithic
memory_manager = MemoryManager(config, encryption_key=key)
await memory_manager.store(content, memory_type=MemoryType.SEMANTIC_MEMORY)
await memory_manager.search("query")

# Exo: composable protocol-based
from exo.memory import (
    ShortTermMemory, EncryptedMemoryStore, MemUpdateChecker,
    SearchManager, MemoryCategory, HumanMemory
)

store = ShortTermMemory()
encrypted = EncryptedMemoryStore(store, key=aes_key)
dedup = MemUpdateChecker(checker=my_llm_call)
search = SearchManager(stores=[encrypted])

Component Mapping

Agent-Core ComponentExo EquivalentNotes
MemoryType enumMemoryCategory enumSame 5 types + CONVERSATION; uses StrEnum
MemoryItem (single class)MemoryItem typed hierarchySystemMemory, HumanMemory, AIMemory, ToolMemory subclasses
AES-256 encryptionEncryptedMemoryStore wrapperAES-256-GCM via cryptography package; wraps any MemoryStore
MemUpdateCheckerMemUpdateCheckerSame name; returns MergeResult with UpdateDecision enum
Migration systemMigrationRegistry + run_migrations()Registry pattern with Migration dataclass
SearchManagerSearchManagerParallel asyncio.gather() across stores
(no equivalent)MemoryStore protocol@runtime_checkable async protocol for pluggable backends
(no equivalent)MemoryStatus lifecycleDRAFTACCEPTEDDISCARD with validated transitions
(no equivalent)MemoryMetadataFrozen Pydantic model with user_id, session_id, task_id, agent_id
(no equivalent)MemoryEventEmitterWraps stores to emit memory:added, memory:searched, memory:cleared
(no equivalent)Evolution strategiesACEStrategy, ReasoningBankStrategy, ReMeStrategy — composable via >> and |

Key Exo Additions Beyond Agent-Core

Typed Memory Hierarchy — Instead of a single item class, Exo provides specialized subclasses with role-specific fields:

Subclassmemory_typeExtra Fields
SystemMemory"system"(none)
HumanMemory"human"(none)
AIMemory"ai"tool_calls: list[dict]
ToolMemory"tool"tool_call_id, tool_name, is_error

Status Lifecycle — Every MemoryItem has a MemoryStatus with enforced transitions:

code
DRAFT ──→ ACCEPTED ──→ DISCARD
  │                        ▲
  └────────────────────────┘

Memory Evolution Strategies — Composable transformations for memory lists:

  • ACEStrategy — Scores memories with helpful/harmful/neutral counters; prunes low-quality
  • ReasoningBankStrategy — Structured entries with title/description; embedding-based recall
  • ReMeStrategy — Reflection-based memory evolution
  • Compose with >> (sequential) or | (parallel)

3. Side-by-Side Examples

Storing an Encrypted Memory

python
# Agent-core
memory_manager = MemoryManager(config, encryption_key="my-password")
await memory_manager.store(
    content="User prefers dark mode",
    memory_type=MemoryType.USER_PROFILE,
)

# Exo
from exo.memory import (
    EncryptedMemoryStore, ShortTermMemory, HumanMemory,
    MemoryCategory, MemoryMetadata,
)
from exo.memory.encrypted import derive_key

key, salt = derive_key("my-password")
store = ShortTermMemory()
encrypted = EncryptedMemoryStore(store, key=key)

item = HumanMemory(
    content="User prefers dark mode",
    category=MemoryCategory.USER_PROFILE,
    metadata=MemoryMetadata(user_id="user-123"),
)
await encrypted.add(item)  # content encrypted transparently

# Retrieval decrypts automatically
result = await encrypted.get(item.id)
assert result.content == "User prefers dark mode"

Running Deduplication

python
# Agent-core
checker = MemUpdateChecker(llm_call=my_llm)
result = checker.check(new_memory, existing_memories)
if result.decision == "add":
    await memory_manager.store(new_memory)
elif result.decision == "merge":
    await memory_manager.update(result.merged_content)

# Exo
from exo.memory import MemUpdateChecker, UpdateDecision

checker = MemUpdateChecker(checker=my_llm_call, top_k=5)
result = await checker.check(new_item, existing_items)

if result.decision == UpdateDecision.ADD:
    await store.add(new_item)
elif result.decision == UpdateDecision.MERGE:
    merged = new_item.model_copy(update={"content": result.merged_content})
    for old_id in result.delete_ids:
        old = await store.get(old_id)
        if old:
            old.transition(MemoryStatus.DISCARD)
    await store.add(merged)

Running Migrations

python
# Agent-core
# Migrations run automatically on initialization

# Exo
from exo.memory.migrations import Migration, MigrationRegistry, run_migrations

registry = MigrationRegistry()
registry.register(Migration(
    version=1,
    description="Add embedding column",
    up=lambda db: db.execute("ALTER TABLE memories ADD COLUMN embedding BLOB"),
))

applied = await run_migrations(sqlite_store, registry)
# Returns count of newly applied migrations

Unified Search Across Stores

python
# Agent-core
results = await memory_manager.search("dark mode preference")

# Exo
from exo.memory import SearchManager, MemoryCategory

manager = SearchManager(stores=[short_term, long_term, vector_store])
results = await manager.search(
    "dark mode preference",
    category=MemoryCategory.USER_PROFILE,
    limit=10,
)
# Queries all stores in parallel, deduplicates, sorts by newest first

4. Migration Table

Agent-Core PathExo ImportSymbol
openjiuwen.core.memory.MemoryTypeexo.memory.MemoryCategory5-type taxonomy enum (USER_PROFILE, SEMANTIC, EPISODIC, VARIABLE, SUMMARY)
openjiuwen.core.memory.MemoryItemexo.memory.MemoryItemBase class with typed subclasses (SystemMemory, HumanMemory, AIMemory, ToolMemory)
(AES-256 in MemoryManager)exo.memory.EncryptedMemoryStoreAES-256-GCM wrapper for any MemoryStore; encrypts content field only
(PBKDF2 key derivation)exo.memory.encrypted.derive_keyPBKDF2-HMAC-SHA256, 480k iterations, returns (key, salt)
openjiuwen.core.memory.MemUpdateCheckerexo.memory.MemUpdateCheckerLLM-based dedup returning MergeResult with UpdateDecision
(migration in MemoryManager)exo.memory.migrations.run_migrationsApplies pending Migration objects from a MigrationRegistry
(migration tracking)exo.memory.migrations.MigrationRegistryOrdered registry with register() and list_pending()
openjiuwen.core.memory.SearchManagerexo.memory.SearchManagerParallel multi-store search with dedup and recency sort
(no equivalent)exo.memory.MemoryStore@runtime_checkable async protocol: add, get, search, clear
(no equivalent)exo.memory.MemoryStatusLifecycle enum: DRAFT, ACCEPTED, DISCARD
(no equivalent)exo.memory.MemoryMetadataFrozen Pydantic model for scoping (user_id, session_id, task_id, agent_id)
(no equivalent)exo.memory.ShortTermMemoryIn-memory conversation store with scope-based windowing
(no equivalent)exo.memory.long_term.MemoryOrchestratorBatches and coordinates async LLM extraction for long-term storage
(no equivalent)exo.memory.events.MemoryEventEmitterWraps stores to emit memory:added, memory:searched, memory:cleared
(no equivalent)exo.memory.summary.SummaryConfigThreshold-based summarization triggers with SummaryTemplate presets
(no equivalent)exo.memory.evolution.MemoryEvolutionStrategyABC for memory transforms; composable via >> (sequential) and | (parallel)
(no equivalent)exo.memory.evolution.ACEStrategyScore-based memory quality tracking with pruning
(no equivalent)exo.memory.evolution.ReasoningBankStrategyStructured entry storage with embedding-based recall
(no equivalent)exo.memory.evolution.ReMeStrategyReflection-based memory evolution
(no equivalent)exo.memory.backends.sqlite.SQLiteMemoryStoreSQLite backend with JSON metadata indexes
(no equivalent)exo.memory.backends.postgres.PostgresMemoryStoreAsync Postgres backend via asyncpg
(no equivalent)exo.memory.backends.vector.VectorMemoryStoreEmbedding-based similarity search backend

All public symbols are re-exported from exo.memory (the package __init__.py), so from exo.memory import EncryptedMemoryStore works as a convenience import.