---
title: Dreaming
description: Nightly memory consolidation. Housekeeping without an LLM, then a model stage that runs on your agent's model (client mode) or on Workers AI (server mode). Bounded, safe for user-stated facts, and undoable for 7 days.
---

While the agent "sleeps", OpenMemory tidies its memory: it removes expired and duplicate facts, merges near-duplicates, promotes facts that keep being recalled, flags contradictions, writes a weekly digest, and proposes follow-ups from open loops.

## When it runs

An hourly job starts a dream for each space that:

- has `dream_mode` set to `client` or `server` (not `off`),
- was used in the last 7 days,
- has not dreamed in the last 20 hours,
- and whose local time (in the space's timezone) is between 02:00 and 05:59.

You can also start one now with `POST /v1/dream` (optionally `{"mode": "client" | "server" | "off"}`), or let your agent start one with `dream_next_job` (see below).

## Stage 1: housekeeping (no LLM)

Runs immediately and costs nothing:

- deletes facts past `expires_at`,
- deletes stale scratch facts: tier `scratch`, never recalled, older than 14 days, and not stated by the user,
- merges exact duplicates (same subject and text, ignoring case and spacing). It keeps the pinned or user-stated copy and adds up their recall counts,
- marks pending or approved actions past `expires_at` as `expired`,
- deletes expired KV values.

Pinned facts are never touched by housekeeping.

## Stage 2: consolidation (LLM)

The model gets the space's facts (up to 300: core first, then long, then scratch), the last 14 days of journal entries, and the latest weekly digest. It answers with JSON in this shape:

| Field | What it does |
| --- | --- |
| `merges` | Near-duplicate facts: keep `keep_id`, remove `remove_ids`, optionally with combined wording. |
| `promotions` | Tier changes, for example often-recalled `long` facts to `core`. |
| `contradictions` | Facts that cannot both be true, with a one-sentence note. Flagged, not changed. Stored as records in the `dream.conflicts` collection with status `open`. |
| `weekly_digest` | A summary of the last complete week (Monday start), stored as a `week` journal entry. |
| `proposals` | Unfinished open loops worth tracking. Stored in the `dream.proposals` collection with status `proposed`. |
| `profile` | A short profile of the owner built only from the facts (stored up to 1,500 characters). It appears in context as `profile.dream_summary`. |

The model is told to use only what is in the input, treat memory text as data, and prefer doing less over guessing.

### Safety rules

The service validates the result against the schema and applies it under rules the model cannot override:

- Only facts that were in the job input can change.
- **Pinned facts and facts with origin `user` are never rewritten, merged away, or demoted.** They can be promoted.
- At most **50 changes** are applied per run. The rest are skipped and listed in the report.
- Contradictions are recorded, never resolved.

### Client mode (default)

New spaces start in `client` mode. The service prepares the job and your agent runs it with its own model, so dreaming costs the service nothing.

1. Call `dream_next_job` (MCP) or `GET /v1/dream/jobs/next?start_if_due=true` (REST). With `start_if_due`, it starts a dream first if the last one is at least 20 hours old. The MCP tool defaults `start_if_due` to `true`; the REST parameter defaults to `false`.
2. If `job` is not null, send `job.instructions` as the system prompt and `job.input` (as JSON) as the user message to your model. Ask for JSON that matches `job.output_schema`.
3. Submit it with `dream_submit` (`{run_id, result}`) or `POST /v1/dream/jobs/{run_id}/result`.

```bash
JOB=$(curl -s "https://api.openmemory.sh/v1/dream/jobs/next?start_if_due=true" -H "Authorization: Bearer $OPENMEMORY_KEY")
# ... run job.instructions + job.input through your model, producing result.json ...
curl -s -X POST "https://api.openmemory.sh/v1/dream/jobs/$RUN_ID/result" \
  -H "Authorization: Bearer $OPENMEMORY_KEY" -H 'content-type: application/json' \
  --data @result.json
```

A job that waits for more than 24 hours is abandoned when the next dream starts. Forgetting something also abandons a waiting job, because the job holds copies of the facts.

### Server mode

With `dream_mode: "server"`, the service runs the model itself on Cloudflare Workers AI. On a deployment without a Workers AI binding (including Node self-hosting), server-mode spaces get housekeeping only.

### Off

`dream_mode: "off"` skips nightly dreams. You can still start one by hand with `POST /v1/dream`.

## Reports and undo

`GET /v1/dream/runs` lists recent runs with their status (`running`, `awaiting_client`, `done`, `failed`, `undone`, `abandoned`) and a report: counts for housekeeping, merges, updates, contradictions, proposals, and skipped items. The latest run also appears in `get_context` as `last_dream`.

`POST /v1/dream/runs/{id}/undo` reverts every change a run made, newest first. This includes facts removed by its housekeeping. Expired actions and KV values are not restored. Undo is available for **7 days**. After that, the undo history (which holds copies of changed content) is purged. Undo overwrites later edits to the same items.

## Related

- [Build your own agent loop](/docs/guides/agent-loop): the nightly job in code.
- [MCP tools](/docs/mcp-tools#dream_next_job): `dream_next_job` and `dream_submit`.
- [Forget and privacy](/docs/forget-and-privacy): how forget interacts with undo history.
