Memory System
How AGH stores curated Markdown memory, captures a frozen snapshot at session start, and routes every write through the controller WAL.
- Audience
- Operators running durable agent work
- Focus
- Memory guidance shaped for scanability, day-two clarity, and operator context.
AGH memory is curated Markdown that survives across sessions. It is designed for durable facts that future agents should be able to discover without replaying every old transcript.
The current implementation is intentionally hybrid:
- curated semantic memory (
user,feedback,project,reference) is Markdown-authoritative on disk memory_decisionsis a per-database write-ahead log: every controller decision lands there before any file mutationmemory_eventsis the canonical observability log for memory operationsmemory_catalog_entries,memory_chunks, and FTS5 indexes are derived projectionsmemory_recall_signalsandmemory_consolidationscarry live runtime state for dreaming- there are three scopes:
global,workspace, andagent— agent has two tiers - only the frozen startup snapshot is injected into prompts; new writes become visible to the next session
Runtime Flow
Rendering diagram…
Memory is not streamed into an already-running process. When a session starts, AGH captures a frozen snapshot of the resolved scopes, packages a recall block, prepends the assembled memory section to the agent prompt, and hands that prompt to the ACP subprocess. Writes during the session are durable immediately but only become visible to the next session via a fresh snapshot.
Scopes And Authorities
| Scope | Storage root | Authority |
|---|---|---|
global | $AGH_HOME/memory/ | Cross-workspace user-wide facts. |
workspace | <workspace>/.agh/memory/ | Workspace-private project facts. |
agent (workspace) | <workspace>/.agh/agents/<agent>/memory/ | Default agent tier. Workspace-private agent state. |
agent (global) | $AGH_HOME/agents/<agent>/memory/ | Cross-workspace agent state. Explicit --agent-tier global. |
Read precedence is agent-workspace ▸ agent-global ▸ workspace ▸ global. Identity is keyed by
(type, slug) per scope, and a deeper scope shadows a shallower scope with the same identity. The
runtime never silently merges shadowed entries — see Scopes.
Scope is selected explicitly. --scope agent requires --agent <name> and a validated
--agent-tier {workspace, global}; the agent tier defaults to workspace when omitted. CLI/HTTP/UDS
operator writes that omit --scope fall back to a conservative type-driven default for the
non-agent scopes only: user and feedback → global, project and reference → workspace.
Agent scope must be explicit.
Workspace Identity
Workspace memory is keyed by a stable ULID stored in <workspace>/.agh/workspace.toml:
workspace_id = "01HXJ9YR4Q..."
created_at = "2026-05-04T14:30:00Z"
realpath_at_creation = "/Users/you/dev/checkout-api"The runtime resolves the workspace by walking ancestors for .agh/workspace.toml, reads the ULID,
and uses that as the durable identity for catalog rows, events, decisions, dreaming runs, and
session ledgers. Path-keyed memory is gone — moving the workspace directory does not orphan its
memory because the ULID travels with the directory. See
Workspace Resolver for the lookup cascade.
Four Memory Types
Every curated memory file declares one of four types:
| Type | Use it for | Example |
|---|---|---|
user | Stable user preferences, working style, or recurring personal context. | "Prefer concise PR summaries with risk and test notes." |
feedback | Repeated corrections, review guidance, and quality signals that apply across work. | "Do not weaken tests to match broken behavior." |
project | Decisions, constraints, active architecture, and local project facts. | "This repo keeps docs site pages under packages/site/content/runtime/." |
reference | External references, runbooks, links, or system facts worth re-reading on demand. | "Production logs live in the hosted provider console, not local files." |
The taxonomy is closed. Unsupported types are rejected at the controller boundary.
Memory File Format
Memory files are Markdown with strict YAML frontmatter. The canonical YAML key for the agent name
is agent; JSON and HTTP payloads use agent_name.
| Field | Required when | Meaning |
|---|---|---|
name | yes | Human-readable title shown in list output and useful in index entries. |
description | yes | One concise discovery sentence used by recall and indexing. |
type | yes | One of user, feedback, project, or reference. |
scope | yes | One of global, workspace, agent. |
agent | when scope = agent | Producer agent name. |
agent_tier | when scope = agent | One of workspace, global. |
provenance | optional | Source actor, source sessions, confidence, supersession, timestamps. |
Example feedback memory written through the controller:
---
name: Test Integrity
description: Production bugs must be fixed instead of weakening tests
type: feedback
scope: global
provenance:
source_actor: extractor
source_sessions:
- 01J7VR2Q8MZ4FXWZ8WB7M2A4S0
confidence: high
created_at: 2026-04-12T14:32:11Z
updated_at: 2026-04-12T14:32:11Z
---
If a test reveals incorrect behavior, fix the production code. Do not relax assertions just to make
the suite green.Storage Layout
Each scope has a MEMORY.md index next to its memory documents and a structurally-excluded
_system/ namespace for machine-managed artifacts:
$AGH_HOME/memory/
MEMORY.md
user_review-style.md
feedback_test-integrity.md
_inbox/ # extractor staging (operator-quiet)
_system/
dreaming/
extractor/
extractor/failures/
ad_hoc/
<workspace>/.agh/memory/
MEMORY.md
project_runtime-docs.md
reference_session-events.md
_system/
dreaming/
...
<workspace>/.agh/agents/<agent>/memory/
MEMORY.md
user_pedro-style.md
_system/
..._system/ is reserved. Curated indexing skips it, recall filters it out by default, and the
controller rejects any write that would land directly in a top-level _system_*.md file. Operators
can browse _system/ artifacts explicitly with --include-system on list/show/search/etc.
The Write Controller
Every write — CLI, HTTP, UDS, native tool, extractor, dreaming, file-watcher, provider — passes through the controller:
- Caller submits a
Candidate { workspace_id, scope, agent, agent_tier, origin, frontmatter, content, ... }. - The controller computes a deterministic decision with rule-first lexical+entity-slot logic; an LLM tiebreaker runs only when the rule trace falls in the configured ambiguity band.
- The decision is persisted to
memory_decisions(per-database WAL) before any file mutation, carrying full replay material:target_filename,frontmatter,post_content,post_content_hash,prior_content(for update/delete),idempotency_key, and the rule/LLM trace. - The file mutation lands atomically; the catalog reindexes the affected file; a canonical
memory_eventsrow is appended. - On crash, daemon boot replays unapplied decisions in
decided_atorder. Replay is idempotent byidempotency_keyandpost_content_hash.
There is exactly one write path. Controller-bypassing tools are forbidden; provider-supplied tools
that collide with reserved names are rejected at registration with a memory.provider.collision
event.
Frozen Snapshot And Recall
At session start, AGH captures a frozen snapshot of the resolved memory context (global, workspace, and the agent's two tiers when applicable). The snapshot includes:
- the per-scope
MEMORY.mdindex after staleness banners - a packaged recall block produced by the deterministic recall pipeline (FTS5 unicode + trigram, scope shadow, top-K) when a contextual query is available
- a freshness banner for entries whose age exceeds
memory.recall.freshness.banner_after_days
The snapshot is captured once per session and does not mutate mid-session. Sub-agent sessions inherit the parent snapshot read-only. Manual writes during the session land in the WAL and on disk, but the running session keeps its captured prompt; the next session sees a fresh snapshot.
_system/ artifacts are never injected into the prompt by default. Recall skips ledger files,
extractor inbox/DLQ artifacts, and dreaming output unless the caller explicitly opts in.
Operator And Agent Surfaces
Memory is reachable from CLI, HTTP, UDS, and native tools with parity. The Slice 1 verbs are:
| Capability | CLI | HTTP / UDS | Native tool |
|---|---|---|---|
| List entries | agh memory list | GET /api/memory | agh__memory_list |
| Show one entry | agh memory show <filename> | GET /api/memory/{filename} | agh__memory_show |
| Search recall | agh memory search <query> | POST /api/memory/search | agh__memory_search |
| Operator write | agh memory write | POST /api/memory | n/a |
| Edit | agh memory edit <filename> | PATCH /api/memory/{filename} | agh__memory_propose |
| Delete | agh memory delete <filename> | DELETE /api/memory/{filename} | agh__memory_propose |
| Agent proposal | n/a | controller-backed via POST /api/memory / PATCH /api/memory/{filename} | agh__memory_propose |
| Ad-hoc note | n/a | POST /api/memory/ad-hoc | agh__memory_note |
| Dream trigger | agh memory dream trigger | POST /api/memory/dreams/trigger | agh__memory_dream_trigger |
| Dream listing | agh memory dream show <date-or-run-id> | GET /api/memory/dreams, GET /api/memory/dreams/{dream_id} | agh__memory_dream_list, agh__memory_dream_show |
| Dream retry | agh memory dream retry <run_id> | POST /api/memory/dreams/{dream_id}/retry | agh__memory_dream_retry |
| Dream status | agh memory dream status | GET /api/memory/dreams/status | agh__memory_dream_status |
| Decisions list | agh memory decisions list | GET /api/memory/decisions | agh__memory_decisions_list |
| Decision detail | agh memory decisions show <id> | GET /api/memory/decisions/{decision_id} | agh__memory_decisions_show |
| Decision revert | agh memory decisions revert <id> | POST /api/memory/decisions/{decision_id}/revert | agh__memory_decisions_revert |
| Recall trace | agh memory recall trace <session_id> <turn_seq> | GET /api/memory/recall-traces/{session_id}/{turn_seq} | agh__memory_recall_trace |
| History | agh memory history | GET /api/memory/history | agh__memory_admin_history |
| Health | agh memory health | GET /api/memory/health | agh__memory_health |
| Config metadata | n/a | GET /api/memory/config | n/a |
| Reindex | agh memory reindex | POST /api/memory/reindex | agh__memory_reindex |
| Promote | agh memory promote --from <scope[:tier]> --to <scope[:tier]> | POST /api/memory/promote | agh__memory_promote |
| Reset | agh memory reset | POST /api/memory/reset | agh__memory_reset |
| Reload snapshot | agh memory reload | POST /api/memory/reload | agh__memory_reload |
| Scope inspector | agh memory scope-show | GET /api/memory/scope-show | agh__memory_scope_show |
| Daily logs | agh memory daily ls | GET /api/memory/daily | agh__memory_daily_list |
| Extractor status | agh memory extractor status | GET /api/memory/extractor/status | agh__memory_extractor_status |
| Extractor failures | agh memory extractor list-pending | GET /api/memory/extractor/failures | agh__memory_extractor_failures |
| Extractor replay | agh memory extractor replay --session <id> | POST /api/memory/extractor/retry | agh__memory_extractor_retry |
| Extractor drain | agh memory extractor drain | POST /api/memory/extractor/drain | agh__memory_extractor_drain |
| Provider list | agh memory provider list | GET /api/memory/providers, GET /api/memory/providers/{provider_name} | agh__memory_provider_list, agh__memory_provider_get |
| Provider select | n/a | POST /api/memory/providers/select | agh__memory_provider_select |
| Provider enable | agh memory provider enable <name> | POST /api/memory/providers/{provider_name}/enable | agh__memory_provider_enable |
| Provider disable | agh memory provider disable <name> | POST /api/memory/providers/{provider_name}/disable | agh__memory_provider_disable |
| Session ledger | n/a | GET /api/workspaces/{workspace_id}/memory/sessions/{session_id}/ledger | agh__memory_session_ledger |
| Session replay | n/a | POST /api/workspaces/{workspace_id}/memory/sessions/{session_id}/replay | agh__memory_session_replay |
| Session prune | n/a | POST /api/memory/sessions/prune | agh__memory_sessions_prune |
| Session repair | n/a | POST /api/memory/sessions/repair | agh__memory_sessions_repair |
Entry writes from agents still route through agh__memory_propose and agh__memory_note; both go
through the controller. The operational tools live in the separate agh__memory_admin toolset and
mirror the daemon CLI/API surfaces. There is no agh__memory_read, no agh__memory_history, no raw
agh__memory_write, no raw agh__memory_edit, no raw agh__memory_delete, no agh memory read,
and no agh memory consolidate in this slice — the renamed verbs above own the same intent.
CLI verbs accept -o json and -o jsonl for structured output. Errors are deterministic
{code, message, details} payloads with stable codes such as memory.scope.invalid,
memory.controller.timeout, and memory.provider.collision.
MEMORY.md Indexes
The per-scope MEMORY.md is the prompt-safe table of contents. AGH reads it inside the snapshot,
caps it at [memory.file] max_lines = 200 and max_bytes = 25600, and includes the entries that
fit. Useful index entries are short and point to one file:
- [Review Style](user_review-style.md) — User wants concise review findings with file references first.
- [Runtime Docs Location](project_runtime-docs.md) — Runtime docs live under `packages/site/content/runtime/`.| Behavior | Current implementation |
|---|---|
Missing MEMORY.md | AGH synthesizes an index from memory-file frontmatter for that scope and warns when an existing index is stale. |
| Prompt limits | Index injection is capped by [memory.file] max_lines and max_bytes. |
| Write behavior | Controller writes the file, updates the WAL, reindexes the catalog, and re-renders the scope index so new entries are discoverable. |
| Delete behavior | Deleting a memory file also removes index lines that link to that filename and emits memory.write.committed with op delete. |
| Full entry content | Not injected. Agents fetch full entries on demand with agh memory show <filename> or agh__memory_show. |
Observability
Every controller decision and recall outcome is observable.
| Surface | Use it for |
|---|---|
agh memory health | Enabled state, controller backlog, provider circuit state, dreaming gate status, and per-scope catalog/file counts. |
agh memory decisions list | The Slice 1 truthful audit log: every committed/rejected/shadowed/reverted controller decision with redaction-safe traces. |
agh memory recall trace <session_id> <turn_seq> | The deterministic recall pipeline trace for a specific session turn: candidate set, scoring weights, freshness banners, and shadow-by-id outcomes. |
agh memory extractor status | Queue and useful-work diagnostics: queued/in-flight sessions, active provider sessions, backpressured sessions, coalesced/dropped turns, skipped empty turns, and failure count. |
GET /api/memory/health | Same data as agh memory health over HTTP/UDS. |
GET /api/memory/decisions | Same data as agh memory decisions list with redaction-safe payloads (no post_content, prior_content, or raw LLM responses on the wire). |
GET /api/memory/recall-traces/{session_id}/{turn_seq} | Same data as agh memory recall trace over HTTP/UDS. |
GET /api/memory/extractor/status | Same extractor queue and useful-work diagnostics over HTTP/UDS. |
memory_events rows have stable canonical op names (memory.write.committed,
memory.write.rejected, memory.recall.executed, memory.dream.run.promoted,
memory.extractor.completed, memory.provider.collision, etc.). They are queryable through the
event store and feed agh memory health and the web Memory inspector.
Skipped empty extractor turns are recorded as memory.extractor.dropped with
metadata.reason = "empty_snapshot" and redaction-safe counters only. Extractor completion events
carry candidate_count, and controller write decisions remain under memory.write.*.
agh memory history returns the same audit material in the legacy summary shape as a thin
compatibility view over memory_events. Use agh memory decisions list when you need
controller-level detail.
Basic Usage
Write a global preference (operator-only):
agh memory write \
--scope global \
--type user \
--name "Review Style" \
--description "User wants concise review findings with file references first" \
--content "Put blocking findings first. Cite file paths and symbols."Write a workspace decision in the current workspace:
agh memory write \
--scope workspace \
--type project \
--name "Runtime Docs Location" \
--description "Runtime docs live in the Fumadocs runtime collection" \
--content 'Runtime docs are authored under `packages/site/content/runtime/` and build to `/runtime/*`.'Write to a specific agent tier:
agh memory write \
--scope agent \
--agent reviewer \
--agent-tier global \
--type feedback \
--name "Reviewer Tone" \
--description "Reviewer keeps findings short and actionable" \
--content "Lead with the blocker. Cite file:line. Keep lists tight."List and show:
agh memory list
agh memory show project_runtime-docs.md --scope workspaceSearch recall:
agh memory search "review tone" --scope agent --agent reviewer --agent-tier globalTrigger a gated dreaming pass:
agh memory dream triggerRelated Pages
- Scopes explains scope selection, agent-tier rules, and shadowing.
- Dream explains gates, signals, and what
dream triggeractually changes. - Memory Best Practices gives concrete writing and hygiene guidance.
- Memory CLI Reference lists every generated memory command.
Memory
How AGH stores durable Markdown memory across global, workspace, and agent scopes, gates dreaming runs, and routes every write through the controller.
Memory Scopes
How AGH resolves the three memory scopes — global, workspace, and agent — selects the agent tier, applies read precedence, and shadows entries by identity.