Skip to content
AGH RuntimeMemory

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.md indexes 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 pathBehavior
Background tickerThe daemon evaluates gates on memory.dream.check_interval (default 30m).
Session stop hookWhen a non-dream session stops in a known workspace, AGH queues a dream check for that workspace_id.
Manual commandagh memory dream trigger evaluates gates for the resolved workspace.
HTTP / UDS APIPOST /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):

GateDefaultBehavior
Timemin_hours = 24Last successful run must be at least min_hours ago.
Sessionsmin_sessions = 3At least min_sessions completed sessions since the last successful run.
Lockone active runnerAcquires the per-workspace dreaming lock; existing runners block re-entry.
Signalgates.min_unpromoted = 5, gates.min_recall_count = 2, gates.min_score = 0.75Inside 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 = 14 half-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…

Dream output is not injected directly into curated memory. Promotions are real controller decisions; the synthesis report is forensic-only under `\_system/dreaming/`.

The dream agent runs as an ordinary AGH session of type dream. It is special in two ways:

  • AGH starts it with approve-all permissions 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

OutputLocation
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 rowsmemory_decisions with origin = dreaming.
memory.dream.run.started / .promotedmemory_events per run.
Failed runmemory.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
FieldMeaning
enabledTurns dreaming on or off. Memory writes still flow through the controller when off.
agentDedicated curator agent. Defaults to dreaming-curator.
min_hoursMinimum hours between successful runs (Time gate).
min_sessionsMinimum completed sessions since the last run (Sessions gate).
debounceDebounce window for triggers from the session stop hook.
prompt_versionPinned prompt template version for the dreaming session.
check_intervalBackground 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 trigger
curl -X POST http://localhost:2123/api/memory/dreams/trigger \
  -H "Content-Type: application/json" \
  -d '{"workspace_id":"01HXJ9YR4QABCDEFGHJK0123456"}'

Possible outcomes:

OutcomeMeaning
triggered: trueA dream session ran. Promotions land via controller decisions.
triggered: false, gate: timeLast successful run is still within min_hours.
triggered: false, gate: sessionsNot enough completed sessions since the last run.
triggered: false, gate: lockAnother dream run for this workspace holds the lock.
triggered: false, gate: signalInside the lock, no candidate cleared the signal threshold.
triggered: false, disabledDreaming is disabled by config or composition.
error responseWorkspace 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_decisions row.
  • It does not inject _system/dreaming/<date>-*.md artifacts 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_persisted hook 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).

On this page