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

TypeFieldDefault pathPurposePruned after
corecore_files(explicit list)Always-loaded persona, standing rules, project invariantsnever
proceduralprocedural_dirmemory/proceduralAppend-only logs: skill records, failure notes, patterns90 days
episodicepisodic_dirmemory/episodicPer-task scratch, transient observations7 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 with mtime older than 7 days
  • procedural → delete files with mtime older than 90 days
  • core → 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.

OpBehavior
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