memory
v0.1.5Durable cross-session agent memory — named banks of always-injected markdown rules and auto-extracted memories, hybrid BM25 + entity recall, pinning, and supersede-never-delete history. Plain files on disk; every operation is a traced function.
- macOS: arm64 · x64
- Linux: arm64 · armv7 · x64
- Windows: arm64 · x64 · x86
exact versions are immutable; binary and bundle artifacts are digest-pinned.
skill doc
memory
Cross-session memory is automatic here. The session's bank injects its rules
and relevant memories into your context each turn, new memories are extracted
in the background after each turn, and standing instructions (style directives,
workflow corrections) graduate into the bank's auto-managed learned rule on
their own. When the user asks you to remember something, acknowledge it — no
save call is required, though an explicit memory::save makes it durable
immediately instead of after the turn.
Rules are markdown documents injected whole into the system prompt on every
turn (memory::rule::*, on disk under the bank's rules/ folder). Memories
are recalled records, ranked against the current question
(memory::save/list/recall). Durable identity-grade guidance belongs in a
rule; point-in-time information belongs in memories.
When to Use
- The user says "remember X", or corrects you in a way that should stick —
save it now:
memory::save { "text": "...", "entities": [...], "pinned": true }. Pin anything they explicitly ask to keep. Re-saving reinforces, never duplicates. - The user asks what you remember about a topic —
memory::recall { "query": "..." }returns the exact ranked memories a turn on that topic would be given. - The user asks to fix or forget something — find it via recall/list, then
memory::update(corrected text) ormemory::delete(tombstone), andmemory::pinto protect the corrected version. - The user wants a style or convention set enforced everywhere —
memory::rule::set { "bank": "...", "name": "style", "content": "..." }. - Separate contexts deserve separate banks (
blogvscoding): a session selects its bank via session metadatamemory_bank(session::set-meta) or the console composer's bank picker;memory::bank::createmakes one. - Memory seems absent —
memory::doctor {}runs a real save→recall roundtrip and names which sibling (router, session-manager) is degraded.
Boundaries
- Not a transcript store: session history lives in session-manager; memory keeps distilled records that point back at their source session.
- Not per-turn context compression: that is context-manager's job. This worker is the durable cross-session sibling.
- Deletes are tombstones and bank deletion moves to
.trash/— nothing is destroyed;include_superseded: trueon list/recall shows history. - Extraction never edits or removes existing memories (ADD-only, fingerprint
reinforced); dedup/merge/promote hygiene belongs to the
memory-consolidatesibling worker. - Injection is bounded (
max_rule_chars,recall_budget_tokens) — an over-budget rule is truncated visibly, never silently dropped.
Functions
memory::save— explicit save; content-fingerprinted, repeat saves reinforce.memory::get / list / update / delete / pin— memory CRUD; delete tombstones, update bumps a revision, pinned records are untouchable by automatic paths.memory::recall— rank a bank's memories against a query (the injection hook's exact scorer); response names the retrieval mode that ran;tagnarrows to one label.memory::preview— the FULL injection dry-run for a hypothetical chat message: the system-prompt rules section, the memories a turn would be handed (floor + budget applied), and the appended message verbatim.memory::tags— a bank's distinct tags with counts. Addtagson save when the user wants memories organized within a bank.memory::bank::create / list / delete— named memory scopes; delete trashes.memory::rule::list / set— always-injected markdown rules; empty content removes a rule.memory::supersede— retire one memory in favor of another (tombstone + pointer); the consolidation seam, agent-denied by default.memory::doctor— end-to-end self-test plus sibling reachability.memory::reload— re-read every bank from disk after hand-editing files.
Reactive triggers
The worker emits two trigger types; bind to them for live views instead of
polling memory::list / memory::bank::list.
- Why bind —
memory::item-changedfires on every memory create / update / supersede / delete ({ event_type, bank, memory });memory::bank-changedfires on bank create / trash and rule changes ({ event_type, bank }). A consumer that mutates memory already has the new record in its return payload — bind only when you need to observe changes made by OTHERS (extraction, other sessions, the console). - When to bind — a UI or index that must stay current with background
extraction; a worker reacting to new memories in a specific bank (use the
bankfilter in the binding config). - How to bind — two steps: register a handler function, then register a
trigger of the wanted type pointing at it, optionally with
config: { "bank": "blog" }to filter. Delivery is fire-and-forget, at-least-once, unordered.