← Back to Plugins
Tools

Formative Memory

jarimustonen By jarimustonen ⭐ 3 stars 👁 33 views ▲ 0 votes

Memory plugin for OpenClaw. Agent's memories strengthen through use, fade when unused, and consolidate overnight.

GitHub

Install

npm install          #

Configuration Example

{
  "plugins": {
    "entries": {
      "formative-memory": {
        "enabled": true,
        "config": {
          "autoRecall": true,
          "autoCapture": true,
          "requireEmbedding": true,
          "embedding": {
            "provider": "auto"
          },
          "consolidation": {
            "notification": "summary"
          },
          "temporal": {
            "notification": "summary"
          }
        }
      }
    }
  }
}

README

# Formative Memory

**Associative memory for [OpenClaw](https://openclaw.ai) agents.**

Formative Memory is an OpenClaw plugin that gives your agent long-term memory modeled after how biological memory works. Before every response, it recalls relevant memories into context. After every response, it evaluates which memories actually contributed — strengthening useful ones and letting unused ones fade. Every night, the agent sleeps: a consolidation process decays, prunes, merges, and connects memories to keep recalled context high-quality.

Over time, raw facts combine into richer structures: merged summaries, connected associations, and deeper understanding.

[![npm version](https://img.shields.io/npm/v/formative-memory)](https://www.npmjs.com/package/formative-memory)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)
![Node.js](https://img.shields.io/badge/Node.js-%E2%89%A522.12-green)

---

## How It Works

### Recall

Before every response, the plugin searches for memories relevant to
the current conversation using hybrid search (embedding similarity +
BM25 full-text), ranked by memory strength. Matching memories are
injected into the agent's context automatically. Each retrieval
strengthens the memories that were surfaced.

```
User: "Do you remember that restaurant? The one by the beach
       last summer. I'm trying to book for our anniversary."

Injected memories:
  [a3f2|fact|strength=0.82]  "Dinner at Maininki, Hanko, April 2026"
  [b7d1|event|2026-07-04]    "Wedding anniversary — 12 years"
  [c9f3|preference|str=0.74] "Sanna loves peonies"
  [d2e6|fact|2026-07-02]     "Sanna: private doctor's appointment"

Agent: "Of course! It was Maininki, in Hanko. Shall I book a table?
        Your 12th anniversary is coming up on July 4th."
```

The agent sees recalled memories as context, not instructions — this
reduces prompt injection risk from stored content.

### Evaluate

After each response, the plugin tracks which memories were surfaced
and whether they actually influenced the reply. This happens at two
levels:

- **Automatic attribution** — the plugin logs which memories were
  injected and which the model referenced, building a retrieval
  history without any agent effort
- **Explicit feedback** — the agent can call `memory_feedback` to
  rate a memory's usefulness (1–5), signaling quality directly

Both signals feed into consolidation: frequently used, highly rated
memories are reinforced, while memories that are surfaced but never
referenced gradually lose strength. This creates a feedback loop where
the memory system learns what is actually useful, not just what matches
a query.

```
After the agent's response about Maininki:

Automatic attribution (logged by the plugin):
  ✓ [a3f2] "Dinner at Maininki, Hanko"     — referenced in reply
  ✓ [b7d1] "Wedding anniversary — 12 years" — referenced in reply
  · [c9f3] "Sanna loves peonies"            — injected, not used
  · [d2e6] "Sanna: doctor's appointment"    — injected, not used

  → a3f2 and b7d1 are reinforced at next consolidation
  → c9f3 and d2e6 were surfaced but ignored — no reinforcement

Explicit feedback (agent calls memory_feedback):
  → memory_feedback(memory_id: "a3f2", rating: 5)
  → "Directly answered the user's question"
```

### Capture

Memories are collected in two ways. The agent can store a memory
explicitly with `memory_store`, and auto-capture extracts durable
facts from conversations automatically after each turn.

```
After the turn above, auto-capture extracts:
→ store("Booking anniversary dinner at Maininki", type: event,
        temporal_anchor: 2026-07-04, temporal_state: future)

A later turn — user asks for Sanna's favorite foods:
Agent explicitly stores:
→ memory_store("Sanna's favorites: salmon soup (her mother's recipe,
   no cream), pistachio ice cream, meat pies from Market Hall
   on Saturdays", type: preference)
→ id: e8b2a1f4, strength: 1.0
```

Each memory is content-addressed (SHA-256) — same content always
produces the same ID, so duplicates are prevented by design.

### Consolidate

Every night, the agent sleeps. A consolidation process runs through
the accumulated memories:

| Step | What happens |
|------|-------------|
| **Reinforce** | Memories that influenced responses gain strength |
| **Decay** | All strengths decrease — recent memories fade faster than established ones |
| **Associate** | Memories retrieved together form links; connections grow stronger with co-occurrence |
| **Temporal shift** | Future memories transition to present or past based on anchor dates |
| **Prune** | Weak memories and associations are removed |
| **Merge** | Similar memories are combined into coherent summaries |

```
Before consolidation:
  [a3f2|strength=0.82] "Dinner at Maininki, Hanko, April 2026"
  [f1c4|strength=0.65] "Maininki — beachfront restaurant, good wine list"
  [a9b3|strength=0.41] "Tried booking Maininki in June, fully booked"

After consolidation:
  [g7e2|strength=1.00] "Maininki, Hanko: beachfront restaurant with good
   wine list. Visited April 2026. Book early — fills up in summer."

  Associations formed:
    "Maininki" ←0.7→ "Wedding anniversary"
    "Maininki" ←0.4→ "Sanna loves peonies"
```

All mutation happens during consolidation — live chat stays fast and
predictable. Over time, simple facts combine into richer structures:
merged summaries, connected associations, and deeper understanding.

## Quick Start

Install the plugin:

```bash
openclaw plugins install formative-memory
```

This installs from npm, enables the plugin, and assigns it the memory
slot automatically. Restart the gateway to load the plugin.

That's it. The plugin works out of the box:

- **Auto-capture** records conversations for consolidation
- **Auto-recall** surfaces relevant memories before every response
- **Consolidation** runs automatically to maintain memory quality
- **Startup tasks** (migrating existing memory files, scrubbing legacy
  memory instructions from `AGENTS.md`/`SOUL.md`, backfilling embeddings)
  run automatically at gateway boot

No configuration needed — sensible defaults are built in.

> **Multi-agent setups:** automatic startup tasks at boot resolve API
> keys from `<stateDir>/agents/main/agent/auth-profiles.json` because
> OpenClaw's plugin service context does not yet expose the active
> `agentDir`. This is correct for the default single-agent `main`
> setup. If you run multiple agents and the primary profile lives
> elsewhere, the first tool call in a session (which carries the
> correct `agentDir`) will pick up the right profile, so migration
> and cleanup still complete — they just defer until that first call.

## Memory Tools

The plugin registers five tools the agent can use during conversation:

| Tool | What it does |
|------|-------------|
| `memory_store` | Store a new memory with type and optional temporal anchor |
| `memory_search` | Search by meaning and keywords, ranked by relevance x strength |
| `memory_get` | Retrieve a specific memory by ID |
| `memory_feedback` | Rate a memory's usefulness (1-5) — feeds into reinforcement |
| `memory_browse` | Browse all memories sorted by importance, with type diversity |

Memory types: `fact`, `preference`, `decision`, `plan`, `observation`.

## Configuration

All settings are optional — defaults are designed to work out of the box.
Configuration goes in `openclaw.json` under the plugin entry:

```json
{
  "plugins": {
    "entries": {
      "formative-memory": {
        "enabled": true,
        "config": {
          "autoRecall": true,
          "autoCapture": true,
          "requireEmbedding": true,
          "embedding": {
            "provider": "auto"
          },
          "consolidation": {
            "notification": "summary"
          },
          "temporal": {
            "notification": "summary"
          }
        }
      }
    }
  }
}
```

| Key | Default | Description |
|-----|---------|-------------|
| `autoRecall` | `true` | Inject relevant memories into context before every response |
| `autoCapture` | `true` | Automatically capture conversations for consolidation |
| `requireEmbedding` | `true` | Require a working embedding provider. Set `false` to allow BM25-only fallback |
| `embedding.provider` | `"auto"` | Embedding provider: `auto`, `openai`, `gemini`. Additional providers (`voyage`, `mistral`, `ollama`, `local`) are also accepted when memory-core embedding adapters are installed as a fallback registry |
| `embedding.model` | — | Override the provider's default embedding model. Only takes effect with an explicit `embedding.provider` — ignored in `"auto"` mode to avoid passing a provider-specific model name to the wrong provider |
| `dbPath` | `~/.openclaw/memory/associative` | SQLite database location |
| `verbose` | `false` | Enable debug logging |
| `consolidation.notification` | `"summary"` | Notification after nightly consolidation: `"off"`, `"summary"` (LLM-generated, persona-aware), or `"detailed"` (raw technical report) |
| `temporal.notification` | `"summary"` | Notification after temporal transitions (15:00 daily): `"off"`, `"summary"`, or `"detailed"` |
| `logQueries` | `false` | Include raw query text in debug logs (disabled by default for privacy) |

The `"auto"` provider selects the best available embedding provider from
your configured API keys. When `requireEmbedding` is `true` (the
default), the plugin will not start without a working embedding provider.
Set it to `false` to allow graceful degradation to keyword-only search.

### API Keys

API keys are read from OpenClaw's `auth-profiles.json`. Environment
variables are **not** used. Configure a profile under the standard
OpenClaw setup:

```json
{
  "version": 1,
  "profiles": {
    "openai:default": { "type": "api_key", "key": "sk-..." },
    "google:default": { "type": "api_key", "key": "AIza..." }
  }
}
```

The `openai:default` and `google:default` profile nam

... (truncated)
tools

Comments

Sign in to leave a comment

Loading comments...