Dream
How AGH gates dreaming runs, scores recall signals, promotes durable memory through the controller, and persists dreaming artifacts under `_system/`.
- Audience
- Operators running durable agent work
- Focus
- Memory guidance shaped for scanability, day-two clarity, and operator context.
Dreaming is AGH's background promotion loop. A dream session reviews recall-validated signal,
proposes durable curated memory through the same controller every other write uses, and writes
forensic artifacts under _system/dreaming/.
The goal is not to compress every transcript. The goal is to graduate facts the system already believed were useful — facts referenced repeatedly during recall — into durable memory:
- promote signal that has been recalled often enough to matter
- merge near-duplicates through controller updates
- keep
MEMORY.mdindexes tight after promotions - record forensic artifacts and DLQ entries for failed runs
Operator Term
In Slice 1 the operator-facing verb is dream trigger. The previous "consolidation" surface is
gone. Use agh memory dream trigger and POST /api/memory/dreams/trigger. There is no
agh memory consolidate, no POST /api/memory/consolidate route, and no --consolidate flag.
What Triggers A Dream
Dreaming is enabled by default when memory is enabled.
| Trigger path | Behavior |
|---|---|
| Background ticker | The daemon evaluates gates on memory.dream.check_interval (default 30m). |
| Session stop hook | When a non-dream session stops in a known workspace, AGH queues a dream check for that workspace_id. |
| Manual command | agh memory dream trigger evaluates gates for the resolved workspace. |
| HTTP / UDS API | POST /api/memory/dreams/trigger accepts an optional workspace selector. |
A trigger always re-evaluates gates. If the gates do not pass, the response is successful and reports the gate that blocked the run.
Gate Cascade
Gates run in this order (cheaper first, observable each step):
| Gate | Default | Behavior |
|---|---|---|
| Time | min_hours = 24 | Last successful run must be at least min_hours ago. |
| Sessions | min_sessions = 3 | At least min_sessions completed sessions since the last successful run. |
| Lock | one active runner | Acquires the per-workspace dreaming lock; existing runners block re-entry. |
| Signal | gates.min_unpromoted = 5, gates.min_recall_count = 2, gates.min_score = 0.75 | Inside the lock, AGH counts unpromoted candidates with sufficient recall hits and minimum score. |
Time and Sessions are evaluated outside the lock. Signal is evaluated after the lock is
acquired so anti-thrash stamps update even when no candidates qualify.
Recall signals are live: every non-trivial recall query updates memory_recall_signals rows
(recall_score, freshness barrier, promotion columns) without affecting the recall response. The
score is a weighted combination of:
- frequency (how often the entry surfaces)
- relevance (BM25 unicode + trigram score)
- recency (with
scoring.recency_half_life_days = 14half-life) - freshness (penalty for stale, never-promoted entries)
Weights live under [memory.dream.scoring.weights] and default to
{ frequency = 0.30, relevance = 0.35, recency = 0.20, freshness = 0.15 }.
What A Dream Run Does
Rendering diagram…
The dream agent runs as an ordinary AGH session of type dream. It is special in two ways:
- AGH starts it with
approve-allpermissions for the dreaming workspace context. - The session prompt is the embedded dreaming-curator prompt at version
memory.dream.prompt_version = "v1".
The agent that runs the dream session is configured by memory.dream.agent and defaults to the
bundled dreaming-curator agent. It does not inherit [defaults].agent.
What Lands Where
| Output | Location |
|---|---|
| Curated promotions | <scope>/memory/<filename> via the controller; observable as memory.write.committed rows. |
| Synthesis artifact | <scope>/memory/_system/dreaming/<YYYYMMDD>-<run-slug>.md. |
| Promoted decision rows | memory_decisions with origin = dreaming. |
memory.dream.run.started / .promoted | memory_events per run. |
| Failed run | memory.dream.run.failed event plus <scope>/memory/_system/dream/failures/<run-id>.md DLQ entry. |
_system/dreaming/ and _system/dream/failures/ are structurally excluded from prompt injection
and from default recall. Operators browse them with agh memory dream show <date> and
agh memory dream retry <run-id>.
Configuration
[memory.dream]
enabled = true
agent = "dreaming-curator"
min_hours = 24
min_sessions = 3
debounce = "10m"
prompt_version = "v1"
check_interval = "30m"
[memory.dream.gates]
min_unpromoted = 5
min_recall_count = 2
min_score = 0.75
[memory.dream.scoring]
recency_half_life_days = 14
[memory.dream.scoring.weights]
frequency = 0.30
relevance = 0.35
recency = 0.20
freshness = 0.15| Field | Meaning |
|---|---|
enabled | Turns dreaming on or off. Memory writes still flow through the controller when off. |
agent | Dedicated curator agent. Defaults to dreaming-curator. |
min_hours | Minimum hours between successful runs (Time gate). |
min_sessions | Minimum completed sessions since the last run (Sessions gate). |
debounce | Debounce window for triggers from the session stop hook. |
prompt_version | Pinned prompt template version for the dreaming session. |
check_interval | Background ticker interval. |
gates.* | Signal-gate thresholds. |
scoring.* | Score weights and the recency half-life used by the signal gate. |
Set [memory.dream] enabled = false to keep memory writes/recall active while disabling background
and manual dream runs.
Manual Trigger
agh memory dream triggercurl -X POST http://localhost:2123/api/memory/dreams/trigger \
-H "Content-Type: application/json" \
-d '{"workspace_id":"01HXJ9YR4QABCDEFGHJK0123456"}'Possible outcomes:
| Outcome | Meaning |
|---|---|
triggered: true | A dream session ran. Promotions land via controller decisions. |
triggered: false, gate: time | Last successful run is still within min_hours. |
triggered: false, gate: sessions | Not enough completed sessions since the last run. |
triggered: false, gate: lock | Another dream run for this workspace holds the lock. |
triggered: false, gate: signal | Inside the lock, no candidate cleared the signal threshold. |
triggered: false, disabled | Dreaming is disabled by config or composition. |
error response | Workspace resolution, controller, lock acquisition, or dreaming session start failed. |
What Dream Does Not Do
- It does not bypass the controller. Every promotion is a real
memory_decisionsrow. - It does not inject
_system/dreaming/<date>-*.mdartifacts directly into prompts; promoted facts must land as curated entries to reach a future snapshot. - It does not replace the extractor. Mid-session candidate extraction is the
session.message_persistedhook and the bounded extractor queue. Dreaming consumes the resulting signal and the_inbox/material. - It does not touch session ledgers. Forensic ledgers are immutable after materialization (see Sessions).
Related Pages
- Memory System explains the controller, the WAL, and recall.
- Memory Scopes explains scope/tier resolution and shadowing.
- Hooks documents
session.message_persistedand the extractor entry point. - Memory Dream Trigger CLI shows the generated command reference.