Agent Memory (typed)
Issue #97 introduces a structured agent-memory layer. Memory stays as plain
markdown files inside each agent's memory/ directory — no vector DB, no
external service. The schema just declares three named types and the
runner / aide memory CLI knows how to manage them.
The three types
| Type | Field | Default path | Purpose | Pruned after |
|---|---|---|---|---|
core | core_files | (explicit list) | Always-loaded persona, standing rules, project invariants | never |
procedural | procedural_dir | memory/procedural | Append-only logs: skill records, failure notes, patterns | 90 days |
episodic | episodic_dir | memory/episodic | Per-task scratch, transient observations | 7 days |
Configure in your Aidefile:
[memory]
compact_after = "200k"
core_files = ["memory/persona.md", "memory/rules.md"]
procedural_dir = "memory/procedural"
episodic_dir = "memory/episodic"
All three are optional and backward-compatible — existing Aidefiles keep working with the documented defaults.
Pruning
aide memory prune # apply defaults across every registered agent
aide memory prune --dry-run # preview only
Type-specific defaults:
episodic→ delete files withmtimeolder than 7 daysprocedural→ delete files withmtimeolder than 90 dayscore→ never touched
.lock sentinel files are skipped. Errors on individual files are swallowed
so one bad file does not abort the whole pass.
Failure distillation
When aide run exits with a non-success status (partial or timeout), the
runner appends a structured note to <procedural_dir>/failures.md:
## 2026-04-18T12:00:00+00:00
- **Issue**: local
- **Task snippet**: <first 200 chars of task>
- **Tokens**: 12345
- **Why it failed**: partial
The append is best-effort — it never fails the run itself. This forms the
substrate for ReMe-style failure distillation: cron-fed jobs can later
summarize failures.md into longer-term procedural notes.
Typed memory ops (text2mem-style)
The memory_ops module provides five free functions over a single agent's
memory/ dir. All ops are markdown-aware and stay file-local.
| Op | Behavior |
|---|---|
merge(path, content, dedupe) | Append + dedupe (none, line_hash, section_replace) |
promote(src, dst) | Atomic rename — typically episodic → core |
demote(src, dst) | Atomic rename — opposite direction |
expire(path, max_age) | Delete if mtime older than max_age; missing file = no-op |
lock(path) / unlock(path) | Advisory <path>.lock sentinel (cooperative; not OS-enforced) |
Dedupe strategies
none— straight append.line_hash— skip lines whose exact text is already present.section_replace— for markdown with## headings, replace any block whose heading matches; preserve everything else.
Lock semantics (limitation)
lock(path) writes <path>.lock with O_CREAT|O_EXCL. A second lock on
the same file fails until unlock removes the sentinel. This is purely
cooperative — code that does not call lock/unlock will happily ignore
the sentinel and clobber the file. It is intended for protecting batch
mutations across the same agent dir, not for cross-process exclusion against
arbitrary writers.
What is intentionally out of scope
- Cross-agent shared memory (separate ticket, needs more design)
- Token-usage benchmarks vs full context (separate eval ticket)
- Vector retrieval / RAG — explicitly excluded by the issue