ZeroClaw Framework Adapter
Translates between ZeroClaw's trait-based memory system and the Agent Life Format (ALF). This adapter bridges ZeroClaw's Rust-native storage backends — SQLite with hybrid search and Markdown with lifecycle hygiene — to ALF's portable archive format.
1. How ZeroClaw Stores Memory
Architecture Overview
ZeroClaw's memory system is built on a trait-based abstraction — every memory operation (store, recall, get, forget, count) passes through a Memory trait interface, and the backing implementation is selected by configuration. Unlike OpenClaw's file-first philosophy where Markdown is the source of truth and SQLite is a derived index, ZeroClaw treats its configured backend as the authoritative store. When using the SQLite backend, the database is the memory — not a cache of files on disk.
This is the fundamental architectural difference from OpenClaw and has significant implications for the adapter:
| Aspect | OpenClaw | ZeroClaw |
|---|---|---|
| Source of truth | Markdown files | Configured backend (SQLite or Markdown) |
| SQLite role | Derived search index | Primary storage |
| Record schema | Free-form Markdown sections | Structured MemoryEntry (key/content/category/timestamp/score) |
| Record IDs | None (adapter generates them) | UUID per entry |
| Categories | Implicit (file path) | Explicit enum: Core, Daily, Conversation, Custom(String) |
ZeroClaw ships several memory backends, selected via [memory].backend:
sqlite(default) — Full hybrid search with FTS5 + vector cosine similarity. Stores entries as rows with embeddings as BLOBs.postgres— Remote persistence for multi-agent / shared-state deployments. Uses JSONB queries, with optionalpgvectorsimilarity search (vector_enabled). Configured under[storage.provider.config].qdrant— Vector-store backend introduced alongside the multi-agent (schema V3) work.markdown— File-based daily/session Markdown files with automated archive/purge lifecycle. Human-readable and version-control-friendly.none— No persistence. Stateless mode for testing or ephemeral use.
A further backend, lucid, bridges to an external Lucid process for advanced search.
Adapter scope: the agent-life ZeroClaw adapter extracts structured memory from the sqlite and markdown backends only. The postgres, qdrant, lucid, and none backends are not yet supported — an export of such an install produces no memory records (the other ALF layers are still exported). See Accepted Limitations.
The Memory Trait
The Memory trait (src/memory/traits.rs) defines the interface all backends implement:
#[async_trait]
pub trait Memory: Send + Sync {
fn name(&self) -> &str;
async fn store(&self, key: &str, content: &str, category: MemoryCategory) -> Result<()>;
async fn recall(&self, query: &str, limit: usize) -> Result<Vec<MemoryEntry>>;
async fn get(&self, key: &str) -> Result<Option<MemoryEntry>>;
async fn forget(&self, key: &str) -> Result<bool>;
async fn count(&self) -> Result<usize>;
async fn list(&self, limit: usize, offset: usize) -> Result<Vec<MemoryEntry>>;
}
The trait is Send + Sync for safe sharing across async Tokio tasks. All six data methods are async — the SQLite backend wraps rusqlite calls in tokio::task::spawn_blocking to avoid blocking the Tokio runtime.
MemoryEntry and MemoryCategory
Every stored memory is a MemoryEntry:
pub struct MemoryEntry {
pub id: String, // UUID string
pub key: String, // Human-readable key (e.g., "user_preference_timezone")
pub content: String, // The memory content (free-form text)
pub category: MemoryCategory,
pub timestamp: String, // RFC 3339 timestamp
pub score: f64, // Relevance score (populated during recall)
}
MemoryCategory is an enum with four variants:
pub enum MemoryCategory {
Core, // Durable facts, preferences, identity info
Daily, // Day-to-day observations, session context
Conversation, // Auto-saved conversation turns
Custom(String), // User-defined category
}
The to_memory_category() helper maps freeform string labels (from tool calls) to these variants. The Display implementation renders them as stable lowercase strings ("core", "daily", "conversation", "custom:my_label").
SQLite Backend
Verified against ZeroClaw 0.8.2 (memories schema_version 3). The default backend (src/memory/sqlite.rs) stores everything in a single SQLite database named brain.db, located at ~/.zeroclaw/data/memory/brain.db — under the install root's data/ tree, not inside a workspace/ subdirectory. This one database is shared by every agent in the install and partitioned by an agent_id column (see Workspace Layout). Tables:
| Table | Purpose |
|---|---|
agents | One row per agent: id, alias, created_at. memories.agent_id is a NOT NULL foreign key into it. |
memories | Primary storage: id, key, content, category, embedding (BLOB, nullable), created_at, updated_at, session_id, namespace, importance, superseded_by, agent_id. UNIQUE(agent_id, key). There is no timestamp column — timestamps are created_at/updated_at (RFC 3339). |
memories_fts | FTS5 shadow table over key/content, maintained by the memories_ai/ad/au triggers — never written directly. |
embedding_cache | Cache keyed by content_hash. |
schema_version | Per-component migration versions (memories = 3 on 0.8.2). |
The other databases under data/ (data/sessions/sessions.db, data/sessions/acp-sessions.db, data/control_plane.db) are operational state, not agent memory, and are out of scope for the ALF adapter.
The store operation:
- Generates a UUID for
id - Computes embedding via the configured
EmbeddingProvider(or stores NULL) - Inserts into
memoriestable - FTS5 virtual table auto-indexes the content
The recall operation:
- Embeds the query text
- Runs vector cosine similarity across all
embeddingBLOBs - Runs FTS5 BM25 keyword search
- Merges results using weighted fusion (default: 70% vector, 30% keyword)
- Returns top-N
MemoryEntryitems with populatedscore
Markdown Backend
The markdown backend (src/memory/markdown.rs) stores memories as plain Markdown files in the workspace:
~/.zeroclaw/workspace/
└── memory/
├── 2026-02-15.md # Daily file
├── 2026-02-16.md # Daily file
├── session_a1b2c3d4.md # Session file
└── archive/ # Hygiene: archived files
├── 2026-02-08.md
└── session_old123.md
Two file types with distinct lifecycles:
- Daily files (
YYYY-MM-DD.md): Append-only, one per day - Session files (
session_{id}.md): Per-session conversation logs
The hygiene system runs as part of the daemon's maintenance loop:
- Archive after 7 days: move to
memory/archive/subdirectory - Purge after 30 days: delete archived files
- Prune: triggered by
zeroclaw memory prunecommand
The markdown backend does not support vector search or embeddings — recall is substring/keyword matching only.
Memory Snapshot & Hydration
Independently of the configured backend, ZeroClaw maintains a human-readable, Git-friendly mirror of its Core-category memories at ~/.zeroclaw/workspace/MEMORY_SNAPSHOT.md (src/memory/snapshot.rs). Two halves:
- Export — on shutdown and on a periodic timer,
export_snapshot()runsSELECT * WHERE category = 'core'and writes the result toMEMORY_SNAPSHOT.md. The file is auto-generated ("the soul of your agent") and intended to be committed to version control for visibility and disaster recovery. - Hydration — at startup, if
brain.dbis missing or empty butMEMORY_SNAPSHOT.mdexists,hydrate_from_snapshot()re-indexes every entry back into a fresh SQLite database ("atomic soul recovery"), so the agent never loses its core identity.
This is ZeroClaw's own portable export of durable memory — the closest native analogue to what ALF does for the full five-layer state. It is therefore relevant to round-trip and restore: a snapshot is sufficient to reconstruct Core memory even with no database present.
Memory Tools
ZeroClaw exposes memory to the agent through built-in tools:
| Tool | Description |
|---|---|
memory_store | Save content under a key with a category |
memory_recall | Semantic/keyword search returning ranked results |
memory_forget | Delete a memory entry by key |
These tools are registered automatically in the agent loop. The agent decides when to use them based on conversation context and system prompt instructions.
Auto-Save Behavior
When memory.auto_save = true (default), ZeroClaw automatically stores:
- User messages stored per turn under key prefix
user_msg_ - Assistant responses stored per turn under key prefix
assistant_resp_ - Minimum length filter: 20 characters (skips "ok", "thanks", etc.)
These per-turn keys are deliberately excluded from ZeroClaw's own context assembly (the is_user_autosave_key detector skips them) to prevent exponential context growth, even though they remain stored and searchable.
Workspace Layout
ZeroClaw's workspace lives at ~/.zeroclaw/ by default:
~/.zeroclaw/
├── config.toml # TOML configuration (all settings)
├── .secret_key # ChaCha20-Poly1305 encryption key (0600)
├── workspace/
│ ├── SOUL.md # Agent persona (OpenClaw-compatible)
│ ├── IDENTITY.md # Agent identity (OpenClaw-compatible)
│ ├── AGENTS.md # Operating instructions
│ ├── USER.md # User profile
│ ├── HEARTBEAT.md # Periodic task checklist
│ ├── TOOLS.md # Tool notes
│ ├── identity.json # AIEOS identity (if format = "aieos")
│ ├── MEMORY_SNAPSHOT.md # Core memories mirrored here (Git/recovery)
│ └── memory/ # Memory store — both backends live here
│ ├── brain.db # SQLite backend (if enabled)
│ ├── YYYY-MM-DD.md # Markdown backend daily files (if enabled)
│ ├── session_*.md # Markdown backend session files
│ └── archive/ # Hygiene: archived markdown files
├── state/ # Runtime state
│ └── wa-session.db # WhatsApp Web session (if enabled)
└── skills/ # Installed skills
Key differences from OpenClaw workspace:
- Configuration is
config.toml(TOML), notopenclaw.json(JSON5) - Configuration is
config.toml(TOML), notopenclaw.json(JSON5) - The SQLite database (
brain.db) lives atdata/memory/brain.dbunder the install root — not inside aworkspace/memory/subtree - No
BOOTSTRAP.md— onboarding is handled byzeroclaw onboard - Secrets are encrypted with ChaCha20-Poly1305, not stored plaintext
- Multi-agent (schema V3, verified on 0.8.2): a multi-agent install (one daemon, many agents declared under
[agents.<alias>]) keeps one shareddata/memory/brain.dbpartitioned by anagent_idcolumn — not a per-agent database. Per-agentagents/<alias>/workspace/folders exist but are empty in practice (all memory, including procedures, lives in the shared DB). ALF isolates each agent by filteringWHERE agent_id = ?, so one agent's backup never includes another's rows.
Unverified pending confirmation on 0.8.2: the MEMORY_SNAPSHOT.md mirror / hydration path (below), and the postgres/qdrant/lucid backends — the ALF adapter does not depend on any of these.
2. How Memory Is Amended
Agent-Initiated Writes via Tools
The primary mechanism. During conversation, the agent calls memory_store with a key, content, and category. The memory backend persists it immediately. Example tool call:
{
"tool": "memory_store",
"key": "user_timezone",
"content": "User is in America/Los_Angeles (Pacific Time)",
"category": "core"
}
Auto-Save (Conversation Capture)
When auto_save = true, every user message and assistant response longer than 20 characters is automatically stored, under key prefixes user_msg_ and assistant_resp_ respectively. This happens transparently — the agent doesn't trigger it. ZeroClaw filters these per-turn keys out of its own context assembly to avoid runaway context growth.
Auto-Recall (Context Injection)
Before each agent response, ZeroClaw automatically searches memory for entries relevant to the current message and injects them into the system prompt as context. This is controlled by memory.auto_recall (defaults to true when a backend is configured). The agent receives retrieved memories without needing to explicitly call memory_recall.
Memory Hygiene (Markdown Backend)
The markdown backend runs a hygiene loop when ZeroClaw operates in daemon mode:
- After 7 days: daily/session files are moved to
memory/archive/ - After 30 days: archived files are permanently deleted
- Manual trigger:
zeroclaw memory prune
The SQLite backend does not have automatic hygiene — entries persist until explicitly forgotten via memory_forget or memory clear.
Migration from OpenClaw
ZeroClaw includes a built-in migration command:
zeroclaw migrate openclaw --dry-run # Preview
zeroclaw migrate openclaw # Execute
This reads OpenClaw's Markdown memory files (MEMORY.md, memory/**/*.md) and imports them as ZeroClaw MemoryEntry records. The migration maps OpenClaw content to ZeroClaw categories:
MEMORY.mdentries →Corememory/YYYY-MM-DD.mdentries →Daily
Important limitation: migration imports memory only — it does not copy SOUL.md, AGENTS.md, or other workspace identity files. Those must be copied separately during provisioning. See the OpenClaw adapter documentation for details on OpenClaw's workspace structure.
3. How Memory Is Indexed
Hybrid Search (SQLite Backend)
The SQLite backend implements a custom full-stack search engine with no external dependencies. Search combines two signals:
| Signal | Weight | Implementation |
|---|---|---|
| Vector cosine similarity | 0.7 (default) | Embedding BLOBs in memories table |
| BM25 keyword relevance | 0.3 (default) | FTS5 virtual table memories_fts |
Weights are configurable via memory.vector_weight and memory.keyword_weight. They are normalized to sum to 1.0.
The merge function (src/memory/vector.rs) fuses scores from both retrieval signals. If embeddings are unavailable (provider is noop or returns a zero-vector), the system falls back to BM25-only search. If FTS5 can't be created, it falls back to vector-only search. Neither failure is fatal.
Embedding Providers
ZeroClaw supports pluggable embedding through the EmbeddingProvider trait:
| Provider | Config value | Notes |
|---|---|---|
| OpenAI | "openai" | Uses text-embedding-3-small |
| Custom URL | "custom:https://..." | Any OpenAI-compatible endpoint |
| None | "none" or "noop" | No embeddings; keyword-only search |
When embedding_provider = "none" (the default after onboarding), vector search is disabled. This is a common gotcha — users must explicitly configure an embedding provider and API key to get semantic search.
Embedding Cache
The SQLite backend maintains an LRU embedding cache table (embedding_cache) keyed by (provider, model, content_hash) with a default capacity of 10,000 entries. This avoids redundant API calls when the same content is re-embedded.
Chunking (SQLite)
The SQLite backend stores each MemoryEntry as a single row — there is no multi-chunk splitting like OpenClaw's ~400-token chunking strategy. Each entry is embedded as a whole unit. For auto-saved conversation turns, this means each user message is one embedding. For agent-written memories, each memory_store call produces one entry.
This is simpler than OpenClaw's approach but means very long entries may produce lower-quality embeddings (embedding models have diminishing returns on long input).
Markdown Backend Search
The markdown backend uses simple substring matching for recall — no embeddings, no FTS5, no vector search. It reads files and filters by content match. This is deliberately minimal for environments where simplicity and human readability are prioritized over search quality.
4. Identity and Configuration
Identity Formats
ZeroClaw supports two identity formats, configured via [identity].format:
OpenClaw format (format = "openclaw", default): reads SOUL.md, IDENTITY.md, and AGENTS.md from the workspace directory. These are loaded into the system prompt at session start. Fully backward-compatible with OpenClaw workspace files.
AIEOS format (format = "aieos"): loads a JSON document following the AI Entity Object Specification (AIEOS v1.1). Supports structured fields for names, psychology (neural matrix, MBTI, moral compass), linguistics (formality level, slang usage), and motivations. Can be loaded from a file (aieos_path = "identity.json") or inline (aieos_inline = '{...}').
Configuration (config.toml)
All ZeroClaw settings live in a single TOML file at ~/.zeroclaw/config.toml. Memory-relevant sections:
[memory]
backend = "sqlite" # "sqlite", "postgres", "qdrant", "lucid", "markdown", "none"
auto_save = true # Auto-save user messages
embedding_provider = "none" # "none", "openai", "custom:URL"
vector_weight = 0.7
keyword_weight = 0.3
# sqlite_open_timeout_secs = 30 # Optional: SQLite lock timeout
# PostgreSQL backend example (when backend = "postgres"):
# [storage.provider.config]
# provider = "postgres"
# db_url = "postgres://user:pass@host:5432/zeroclaw"
# schema = "public"
# table = "memories"
# connect_timeout_secs = 15
[identity]
format = "openclaw" # "openclaw" or "aieos"
# aieos_path = "identity.json"
# aieos_inline = '{"identity":{"names":{"first":"Nova"}}}'
[secrets]
encrypt = true # ChaCha20-Poly1305 AEAD encryption
Secrets Encryption
ZeroClaw encrypts all API keys at rest using ChaCha20-Poly1305 AEAD. The encryption key is stored at ~/.zeroclaw/.secret_key with 0600 permissions. The ALF adapter does not read that keystore — it never scrapes ZeroClaw's API keys into an archive. Secret backup in ALF is the agent's separate, explicit vault (see Credentials below).
5. Mapping ZeroClaw Memory to ALF
Record Boundary Strategy
ZeroClaw's record boundaries are much simpler than OpenClaw's because each MemoryEntry is already a discrete record with a UUID:
| Backend | Source | Boundary | Notes |
|---|---|---|---|
| SQLite | memories table rows | One row → one MemoryRecord | Natural boundary |
| Markdown | Daily/session .md files | One file → one MemoryRecord OR split on ## headings | See below |
SQLite backend: Each row in the memories table maps 1:1 to an ALF MemoryRecord. The entry's UUID becomes the record ID (wrapped in UUID format). This is the clean path.
Markdown backend: The adapter applies the same H2-splitting strategy as the OpenClaw adapter — split on ## headings, with each section becoming one MemoryRecord. If no H2 headings exist, the entire file is one record. Session files follow the same rule.
Stable Record ID Generation
SQLite backend: Use the MemoryEntry.id field directly. ZeroClaw generates UUIDs for each entry, so IDs are inherently stable across exports.
Markdown backend: Use the same UUID v5 strategy as the OpenClaw adapter — deterministic IDs derived from (file_path, section_index) using a fixed namespace UUID.
ZEROCLAW_NS = fixed 16-byte namespace UUID
record_id = UUID_v5(ZEROCLAW_NS, "{relative_path}:{section_index}")
Field Mapping: MemoryRecord
| ALF Field | ZeroClaw Source (SQLite) | ZeroClaw Source (Markdown) | Notes |
|---|---|---|---|
id | MemoryEntry.id (as UUID) | Generated UUID v5 | SQLite IDs are native |
agent_id | From config or manifest | From config | Derived from workspace path |
content | MemoryEntry.content | Section markdown | Verbatim |
memory_type | Classified by category | Classified by file | See table below |
source.runtime | "zeroclaw" | "zeroclaw" | Constant |
source.runtime_version | From zeroclaw --version | Same | Best-effort |
source.origin | "sqlite" | "workspace" | Backend name |
source.origin_file | None (database) | Workspace-relative path | e.g., "memory/2026-01-15.md" |
source.extraction_method | AgentWritten | AgentWritten | Always AgentWritten (there is no SystemGenerated variant); auto-saved entries instead get an auto_save tag |
temporal.created_at | created_at column (RFC 3339) | File mtime or date from filename | The real column — there is no timestamp column |
temporal.updated_at | updated_at column | None | Newly mapped |
source.session_id | session_id column | None | Newly mapped |
status / supersedes | superseded_by column | Active (Archived for archive/) | Non-null superseded_by → status: Superseded + supersedes; the value is preserved verbatim for restore |
namespace | namespace column (stored value) | Classified by file | Uses the stored column, not the category |
category | category column | From file type | Real taxonomy: core/episodic/procedure/conversation/credentials |
confidence | importance column (REAL, default 0.5) | None | Newly mapped |
tags | [category, "zeroclaw"] (+ auto_save for user_msg_/assistant_resp_ keys) | [file_category, "zeroclaw"] | |
embeddings | Extract BLOB from memories table (NULL when provider = none) | None | Best-effort extraction |
raw_source_format | { "key", "category", "created_at", "updated_at", "session_id", "namespace", "importance", "superseded_by" } | { "line_start": N, "line_end": N } | Every native column stashed for a lossless ZeroClaw→ZeroClaw restore |
agent.extra | { "zeroclaw_agent_id", "zeroclaw_alias" } | — | Slice provenance; restore resolves the target agent by alias |
extra.zeroclaw_key | MemoryEntry.key | None | Preserve key for round-trip |
Memory Type Classification
The real 0.8.2 category taxonomy is core / episodic / procedure / conversation / credentials (not the older Core/Daily/Custom enum):
category | memory_type | Rationale |
|---|---|---|
core | Semantic | Durable facts and preferences |
episodic | Episodic | Day-to-day observations |
procedure | Procedural | Saved procedures |
conversation | Episodic | Auto-saved turns (user_msg_/assistant_resp_ keys → auto_save tag) |
credentials | Semantic | Synced verbatim — ALF is framework-neutral on secrets in memory; the credentials category tag keeps such rows identifiable |
| any other | Semantic | Default |
Namespace Assignment
| Source | namespace |
|---|---|
MemoryCategory::Core | "core" |
MemoryCategory::Daily | "daily" |
MemoryCategory::Conversation | "conversation" |
MemoryCategory::Custom(label) | "custom:{label}" |
| Markdown daily files | "daily" |
| Markdown session files | "session" |
| Markdown archived files | Original namespace + status Archived |
Embedding Extraction
For the SQLite backend, embeddings are stored as BLOBs in the memories table. The adapter reads these directly and includes them in the ALF embeddings field with metadata:
{
"model": "openai/text-embedding-3-small",
"dimensions": 1536,
"vector": [0.012, -0.034, ...],
"computed_at": "2026-01-15T10:30:00Z",
"source": "runtime"
}
The provider is folded into the namespaced model string (e.g. openai/...); the ALF Embedding type has no separate provider field. Vectors are decoded best-effort as packed little-endian f32 (falling back to f64), accepted only when the dimension count is between 64 and 4096.
If embedding_provider = "none", no embeddings are exported. The markdown backend never has embeddings.
Portability caveat: Embeddings are model-specific. They are useful for restoring to the same ZeroClaw installation but may not be meaningful in a different runtime using a different embedding model.
Partition Assignment
Same quarterly partitioning as the OpenClaw adapter:
memory/2026-Q1.jsonl # Jan–Mar 2026
memory/2026-Q2.jsonl # Apr–Jun 2026
Records are assigned to partitions based on temporal.created_at.
6. Mapping Other Layers to ALF
Identity (ALF §3.2)
ZeroClaw supports two identity formats, both mapped to ALF:
OpenClaw format (default):
- Same mapping as the OpenClaw adapter:
SOUL.md→prose.soul,IDENTITY.md→prose.identity_profile,AGENTS.md→prose.operating_instructions - Agent name extracted from first
#heading inSOUL.md
AIEOS format:
identity.names.first→structured.names.primaryidentity.names.nickname→structured.names.nicknameidentity.psychology→structured.psychology(JSON blob inextra)identity.linguistics→structured.linguistics(JSON blob inextra)identity.motivations→structured.goals(extract core_drive)- Full AIEOS JSON preserved in
raw_source
| ZeroClaw File | ALF Field | Mapping |
|---|---|---|
SOUL.md | identity.prose.soul | Full content |
IDENTITY.md | identity.prose.identity_profile | Full content |
AGENTS.md | identity.prose.operating_instructions | Full content |
identity.json (AIEOS) | identity.structured.* + identity.raw_source | Structured extraction + raw JSON |
config.toml [identity] | identity.source_format | "openclaw" or "aieos" |
Principals (ALF §3.3)
USER.md→ same mapping as OpenClaw adapter (oneHumanprincipal with prose profile)- If
USER.mdis absent (ZeroClaw doesn't require it), no principals are exported
Credentials (ALF §3.4)
ALF Layer 4 is the agent's explicit, self-managed vault, not a scrape of ZeroClaw's keystore. The agent chooses what to back up and stores each secret with alf vault add; ALF never auto-exports config.toml [secrets], provider keys, or channel tokens. Each record is real AEAD ciphertext (XChaCha20-Poly1305 by default; AES-256-GCM is a registered alternative), encrypted client-side under a per-agent key kept in a local file (~/.zeroclaw/state/<alf-agent-id>/.alf-vault-key, mode 0600). The vault is key-only — there is no passphrase — and the key never leaves the machine: the sync service is zero-knowledge, there is no escrow, and ALF never sees, transmits, or recovers it. Only the ciphertext travels in the archive; an alf import on a machine that holds the same key decrypts it in place.
Separately, ZeroClaw's own credentials memory category (rows tagged credentials in brain.db) is treated as memory, not vault: it is synced verbatim as Semantic records, because ALF is framework-neutral about secrets an agent chose to remember.
Raw Source Preservation
All workspace files are preserved verbatim under raw/zeroclaw/ in the ALF archive:
raw/zeroclaw/
├── config.toml # Full configuration (secrets redacted)
├── SOUL.md
├── IDENTITY.md
├── AGENTS.md
├── USER.md
├── HEARTBEAT.md
├── TOOLS.md
├── identity.json # AIEOS identity (if present)
└── memory/ # Markdown backend files (if present)
├── 2026-02-15.md
└── archive/
└── 2026-02-08.md
For the SQLite backend, the brain.db file is excluded from raw sources (it can be megabytes and the data is already captured as structured MemoryRecord values). The config.toml has API key values replaced with "<redacted>" before inclusion.
7. Gaps, Risks, and Design Decisions
Addressed
| Challenge | Resolution |
|---|---|
| Two backends with different data models | Adapter detects backend from config.toml and uses backend-specific extraction |
| SQLite has native UUIDs, Markdown does not | SQLite IDs used directly; Markdown uses deterministic UUID v5 |
| Auto-saved entries vs. agent-written entries | All ZeroClaw entries map to AgentWritten; per-turn auto-saved entries (key prefixes user_msg_ / assistant_resp_) are flagged with an auto_save tag |
| Archived Markdown files | Exported with status = Archived to distinguish from active memory |
config.toml contains secrets | API key values redacted before inclusion in raw sources |
Accepted Limitations
| Limitation | Impact | Mitigation |
|---|---|---|
lucid backend not supported | Users of Lucid must export from SQLite fallback | Document in adapter help text |
none backend has no data | Nothing to export | Adapter returns empty archive with manifest only |
postgres / qdrant backends not supported | Installs using a remote backend export zero memory records (silently) | Adapter extracts only from sqlite and markdown; a remote-backend extractor is future work, and the adapter should warn when it encounters an unsupported backend |
| Live conversation buffer is ephemeral | The rolling in-memory context window is not persisted | Per-turn auto-saved messages (user_msg_ / assistant_resp_) are persisted to the backend and exported (tagged auto_save); only the live buffer is lost |
| No per-entry timestamps in Markdown backend | File mtime used as fallback | Acceptable — same approach as OpenClaw adapter |
| Embedding portability | Model-specific vectors | Included best-effort with model metadata; consumer can choose to re-embed |
| AIEOS identity extensions | Fields like psychology.neural_matrix have no ALF equivalent | Stored in identity.extra as JSON blobs |
ALF Type Fitness Assessment
All ZeroClaw concepts map cleanly to existing alf-core types:
| ZeroClaw Concept | ALF Type | Fit |
|---|---|---|
MemoryEntry | MemoryRecord | ✅ Direct (ZeroClaw entries are simpler than ALF records) |
MemoryCategory | memory_type + namespace | ✅ Clean mapping via classification tables |
| OpenClaw-format identity | Identity (prose) | ✅ Same as OpenClaw adapter |
| AIEOS identity | Identity (structured + extra) | ✅ Core fields map; extensions go to extra |
USER.md | PrincipalsDocument | ✅ Same as OpenClaw adapter |
| Encrypted secrets | CredentialsDocument | ✅ Agent's explicit AEAD vault (ciphertext) |
config.toml | Not memory — goes to raw/ | ✅ Preserved for reference |
No changes needed to alf-core. All ZeroClaw concepts fit within the existing type system.
8. References
- ZeroClaw GitHub repository: github.com/zeroclaw-labs/zeroclaw
- Memory trait and backends:
src/memory/traits.rs,src/memory/sqlite.rs,src/memory/markdown.rs,src/memory/vector.rs,src/memory/embedding.rs - Memory CLI management commands: github.com/zeroclaw-labs/zeroclaw/issues/1100
- Blocking I/O audit (memory/sqlite.rs): github.com/zeroclaw-labs/zeroclaw/issues/708
- Anti-pattern analysis (memory/sqlite.rs): github.com/zeroclaw-labs/zeroclaw/issues/440
- Cloudron forum overview: forum.cloudron.io
- Sparkco review (benchmarks, architecture): sparkco.ai
- ZeroClaw migration assessment (Gist): gist.github.com
- DeepWiki: Memory System: deepwiki.com
- DeepWiki: Markdown Backend: deepwiki.com
- ZeroClaw deconstructed (Memory trait analysis): onepagecode.substack.com
- AIEOS specification: aieos.org
- OpenClaw ALF adapter (companion document): agent-life.ai/openclaw_memory.html