> ## Documentation Index
> Fetch the complete documentation index at: https://sidenet.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Memory

> What an agent remembers — recent messages, long-term recall, standing notes about a user, a background observer that keeps a compact log of the conversation — and how to make it remember a user across conversations and automation runs.

An agent's memory is part of its version, like its tools and guardrails:
edit the draft, publish, activate. It is one block, `memory.options`, with
five independent switches. An agent saved with no memory block gets the
platform defaults: the last ten messages, a title generated from the first
message, nothing else.

| Option | What the agent gets | Default |
| - | - | - |
| `lastMessages` | The last N messages of the conversation, read back on every turn. | 10 |
| `generateTitle` | A short title for the conversation, written from the first message. | on |
| `semanticRecall` | Long-term recall: the past messages closest in meaning to the new one, from any of the user's conversations. | off |
| `workingMemory` | Standing notes about the user — one record per user, shared by every conversation and every automation run of theirs. | off |
| `observationalMemory` | A background observer: a small model reads the conversation as it grows and keeps a dense log of what happened, which replaces raw history once it gets long. It can also maintain the working memory and the title. | off |

Everything below goes through `PATCH /v1/agents/{id}/draft`. An unknown
key or a value out of range answers `400` naming the path, so a typo never
saves as a setting that silently does nothing. `"memory": null` restores the
platform defaults.

## Remembering a user across conversations and runs

A scheduled automation runs in a **new conversation every time** (see
[Result threads](/docs/user-workflows#result-threads)), so nothing thread-scoped
carries from Monday's run to Tuesday's. What does carry is the user's
working memory, when it is kept per user and someone keeps it current. The
combination:

```bash theme={null}
curl -X PATCH https://api.sidenet.ai/v1/agents/AGENT_ID/draft \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "memory": {
      "options": {
        "workingMemory": { "enabled": true, "scope": "resource" },
        "observationalMemory": {
          "enabled": true,
          "observation": {
            "manageWorkingMemory": true,
            "bufferOnIdle": true,
            "threadTitle": true
          }
        }
      }
    }
  }'
```

* **`workingMemory.scope: "resource"`** — one record per user (it is the
  default scope). A user's chats and their automation runs share it: a run
  executes as its owner, on the same user record.
* **`observation.manageWorkingMemory`** — the observer updates the notes in
  the background, from what it observed. The agent no longer has to
  remember to do it mid-answer, and gets the notes read-only in its system
  prompt.
* **`observation.bufferOnIdle`** — the observer also runs at the end of
  every turn, however short. Without it a run's conversation (one user
  message, one answer) would never reach the observer's token interval, and
  the notes would never be written.
* **`observation.threadTitle`** — the observer renames the conversation
  when its topic meaningfully changes, so the thread list follows what was
  actually discussed.

What the user gets: an agent that knows on Tuesday what it learned on
Monday — in the chat where they asked a follow-up as much as in the next
scheduled run — and conversation titles that follow the conversation.

## Working memory

```json theme={null}
"workingMemory": {
  "enabled": true,
  "scope": "resource",
  "template": "# User\n- Name:\n- Role:\n- Preferences:",
  "agentManaged": true
}
```

| Option | Meaning | Default |
| - | - | - |
| `enabled` | On or off. Sending the object with no `enabled` means on. | — |
| `scope` | `resource`: one record per user, every conversation and run; `thread`: one per conversation. | `resource` |
| `template` | A Markdown template of the notes — the headings the agent fills. | a generic profile |
| `schema` | A JSON Schema instead of a template: the notes are kept as a JSON object. Send one or the other. | — |
| `agentManaged` | Whether the agent itself gets a tool and instructions to update its notes during the answer. | `true`, `false` when the observer manages them |

`workingMemory: true` is the same as `{ "enabled": true }`. The notes are
injected into the agent's system prompt on every turn.

## The observer (observational memory)

```json theme={null}
"observationalMemory": {
  "enabled": true,
  "model": "anthropic/claude-haiku-4-5",
  "activateAfterIdle": "5m",
  "observation": {
    "messageTokens": 30000,
    "bufferTokens": 0.15,
    "bufferActivation": 0.8,
    "blockAfter": 1.9,
    "bufferOnIdle": true,
    "manageWorkingMemory": true,
    "threadTitle": true,
    "instruction": "Keep order numbers and delivery dates."
  },
  "reflection": {
    "observationTokens": 40000,
    "bufferActivation": 0.5,
    "blockAfter": 1.9
  }
}
```

`observationalMemory: true` turns it on with every default below.

| Option | Meaning | Default |
| - | - | - |
| `model` | The model that observes and reflects, as a `provider/model` slug. | the platform observer model, a small fast one |
| `scope` | `thread`: the observation log belongs to the conversation. This is the only value. | `thread` |
| `activateAfterIdle` | After this much inactivity, buffered observations are activated before the threshold, so a conversation resumed later starts from a compact context. Milliseconds, a duration (`"5m"`, `"1hr"`), `"auto"` (the model provider's prompt-cache lifetime) or `false`. | off |
| `retrieval` | Lets the agent read the raw messages behind its observations through a `recall` tool. `true`, or `{ "vector": true }` to also search them by meaning, `{ "scope": "thread" }` to keep it to the current conversation, `{ "instructions": "…" }` to guide it. | off |
| `observation.model` | A model for the observer alone (then set `reflection.model` for the reflector, or leave it to follow). Exclusive with the top-level `model`. | — |
| `observation.messageTokens` | How many message tokens a conversation keeps before observations replace raw history. | 30000 |
| `observation.bufferTokens` | How often the observer runs in the background: a fraction of `messageTokens` or a token count. Below one, the observer keeps up and nothing waits on it. | 0.15 (every 4,500 tokens) |
| `observation.bufferActivation` | At the threshold, the share of buffered observations that replaces history (the rest stays in reserve for continuity). | 0.8 |
| `observation.blockAfter` | Multiplier of `messageTokens` past which the observer is run synchronously, as a safety valve — only when one burst (a huge tool result) outruns the background observer. | 1.9 |
| `observation.bufferOnIdle` | Also observe at the end of every turn, however short. | off |
| `observation.manageWorkingMemory` | The observer maintains the working memory. Needs `workingMemory` on. | off |
| `observation.threadTitle` | The observer renames the conversation when the topic meaningfully changes. | off |
| `observation.instruction` | Extra guidance appended to the observer's prompt: what to focus on, what to keep. | — |
| `observation.activateAfterIdle` | Overrides the top-level `activateAfterIdle` for observations; `false` switches it off for them. | inherits |
| `reflection.observationTokens` | How large the observation log gets before the reflector condenses it. | 40000 |
| `reflection.bufferActivation` | The share of the threshold at which background reflection starts. | 0.5 |
| `reflection.blockAfter` | Multiplier of `observationTokens` past which a reflection is forced synchronously. | 1.9 |
| `reflection.activateAfterIdle` | Idle activation for reflections; not inherited from the top level. | off |
| `reflection.instruction` | Extra guidance for the reflector. | — |
| `reflection.model` | A model for the reflector alone. | — |

### Titles: the first one and the rewrites

Two writers can name a conversation. `generateTitle` names it from the
first message, as soon as the first answer is saved, so the thread list is
never blank. With `observation.threadTitle` the observer proposes a title
at the end of its first pass — that one replaces the first — and from then
on rewrites it only when the topic meaningfully changes; it is shown its own
previous title and told to leave it alone otherwise. A rewrite lands on the
thread itself and reaches every open client as a thread update over
[`GET /v1/events`](/docs/user-workflows#building-your-own-ui-on-it), like any
other change to the row.

Turn `generateTitle` off (`false`) to let the observer be the only writer;
the conversation is then untitled until its first observation.

### Cost

The observer, the reflector and the title model run on the organization's
own key, under the billing group of the conversation they belong to (an
automation run's: its owner's group), and show up in that group's usage like
the agent's own calls. The shape, with the defaults above:

* one background observer call every `bufferTokens` worth of new messages
  (every 4,500 tokens), on the conversation's messages since the last one;
* with `bufferOnIdle`, one background observer call at the end of every
  turn — the cost of remembering a user across runs is one small-model call
  per run;
* one reflector call each time the observation log passes its threshold;
* one title call per conversation (`generateTitle`), plus a title rewrite
  only when the observer decides the topic changed — part of its normal
  call, not an extra one.

None of these calls sits on the request path: the answer streams while the
observer works, and the end-of-turn observation starts after the answer is
complete. The one exception is `blockAfter`, the synchronous safety valve.

## Long-term recall (semantic recall)

```json theme={null}
"semanticRecall": { "topK": 5, "messageRange": 2, "scope": "resource" }
```

| Option | Meaning | Default |
| - | - | - |
| `topK` | How many past messages to recall. | 5 |
| `messageRange` | Context around each recalled message: a number of messages, or `{ "before": 1, "after": 1 }`. | 2 |
| `scope` | `resource`: across all of the user's conversations; `thread`: this one only. | `resource` |

`semanticRecall: true` uses the defaults. Each turn costs one embedding of
the new message, and each saved message one embedding, on the platform
embedding model.

## Titles and history

```json theme={null}
"lastMessages": 20,
"generateTitle": { "model": "openai/gpt-4o-mini", "instructions": "Five words, no punctuation." }
```

| Option | Meaning | Default |
| - | - | - |
| `lastMessages` | Recent messages read back on every turn, 0–200. `false` or `0` reads none — a stateless agent that still records its threads. | 10 |
| `generateTitle` | `true`, `false`, or `{ model, instructions }`. | on, platform title model |
| `readOnly` | Read memory but save nothing: no messages, no notes, no observations. | `false` |

## In the Studio

Memory is a section of the agent editor: a switch per option, the
cross-conversation combination above as one preset, and the model and
threshold fields below. The API shape is what the editor saves.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.