Metadata-Version: 2.5
Name: runtime-memory
Version: 4.0.0
Summary: Persistent memory for AI coding agents with outcome-based learning
Project-URL: Homepage, https://github.com/runtimenoteslabs/memory-layer
Project-URL: Documentation, https://github.com/runtimenoteslabs/memory-layer#readme
Project-URL: Repository, https://github.com/runtimenoteslabs/memory-layer
Project-URL: Issues, https://github.com/runtimenoteslabs/memory-layer/issues
Project-URL: Changelog, https://github.com/runtimenoteslabs/memory-layer/blob/main/CHANGELOG.md
Author: exitcode42
License-Expression: MIT
License-File: LICENSE
Keywords: ai,claude,coding-agents,mcp,memory
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: aiosqlite>=0.19.0
Requires-Dist: click>=8.1.0
Requires-Dist: httpx>=0.25.0
Requires-Dist: numpy>=1.24.0
Requires-Dist: pydantic-settings>=2.0.0
Requires-Dist: pydantic>=2.5.0
Requires-Dist: watchdog>=3.0.0
Provides-Extra: all
Requires-Dist: anthropic>=0.18.0; extra == 'all'
Requires-Dist: fastapi>=0.109.0; extra == 'all'
Requires-Dist: mcp>=1.0.0; extra == 'all'
Requires-Dist: rich>=13.0.0; extra == 'all'
Requires-Dist: sentence-transformers>=2.2.0; extra == 'all'
Requires-Dist: uvicorn>=0.27.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: black>=24.1.0; extra == 'dev'
Requires-Dist: httpx>=0.25.0; extra == 'dev'
Requires-Dist: mypy>=1.8.0; extra == 'dev'
Requires-Dist: pre-commit>=3.6.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest>=7.4.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: embedding
Requires-Dist: sentence-transformers>=2.2.0; extra == 'embedding'
Provides-Extra: extraction
Requires-Dist: anthropic>=0.18.0; extra == 'extraction'
Provides-Extra: phase1
Requires-Dist: anthropic>=0.18.0; extra == 'phase1'
Requires-Dist: sentence-transformers>=2.2.0; extra == 'phase1'
Provides-Extra: phase2
Requires-Dist: fastapi>=0.109.0; extra == 'phase2'
Requires-Dist: mcp>=1.0.0; extra == 'phase2'
Requires-Dist: rich>=13.0.0; extra == 'phase2'
Requires-Dist: uvicorn>=0.27.0; extra == 'phase2'
Provides-Extra: server
Requires-Dist: fastapi>=0.109.0; extra == 'server'
Requires-Dist: mcp>=1.0.0; extra == 'server'
Requires-Dist: rich>=13.0.0; extra == 'server'
Requires-Dist: uvicorn>=0.27.0; extra == 'server'
Description-Content-Type: text/markdown

# Runtime Memory

Persistent memory for AI coding agents with outcome-based learning.

> **New to Runtime Memory?** See the [User Guide](https://github.com/runtimenoteslabs/memory-layer/blob/main/USER_GUIDE.md) for an introduction to using Runtime Memory with Claude Code.

## What it does

Runtime Memory stores knowledge from your coding sessions and records how often each memory worked and how often it failed. Among the memories relevant to a query, those with a better record rank higher. A memory that keeps failing is left out of retrieval until its failures fade.

## Installation

```bash
pip install "runtime-memory[all]"
```

The base package stores and searches memories by keyword. Each extra adds a part:

| Extra | Adds | Without it |
|-------|------|------------|
| `embedding` | Semantic search, with `sentence-transformers` | Search is keyword-only |
| `extraction` | Extracting memories from sessions with Claude | No extraction |
| `server` | The MCP server, the REST API and the web UI | `mem serve` is unavailable |
| `all` | All three | |

`mem stats` shows which search mode is in use. With `embedding`, the first search
downloads an embedding model of about 100 MB and caches it.

Or from source:

```bash
pip install "runtime-memory[all] @ git+https://github.com/runtimenoteslabs/memory-layer.git"
```

For development:

```bash
git clone https://github.com/runtimenoteslabs/memory-layer.git
cd memory-layer
pip install -e ".[dev]"
```

The distribution is `runtime-memory` and the import is `runtime_memory`. The
repository is still named memory-layer, which is where the project started; the
package was renamed in 3.0. An unrelated package holds `memory-layer` on PyPI,
so `pip install memory-layer` fetches that one instead of this project.

## Quick start

### Python SDK

```python
from runtime_memory.sdk import MemoryClient

async with MemoryClient() as client:
    # Store a memory
    memory = await client.add(
        content="Use async/await for I/O operations",
        category="pattern",
    )

    # Search memories
    results = await client.search("async patterns", limit=5)

    # Record feedback
    await client.record_outcome(memory.id, "worked")

    # Get context for your project
    context = await client.get_context()
```

### Synchronous client

```python
from runtime_memory.sdk import SyncMemoryClient

with SyncMemoryClient() as client:
    client.add("Always validate user input", category="convention")
    results = client.search("input validation")
```

### CLI

```bash
# Add a memory
mem add "Use type hints for better IDE support" -c convention

# Search memories
mem search "type hints"

# See why a search returns what it does, and why the rest were left out
mem why "type hints"

# Store statistics: outcome records, search mode, and the Hermes trace if present
mem stats

# Record outcome
mem outcome <memory-id> worked

# Get context
mem context

# Start REST API server
mem serve --rest --port 8080

# Start MCP server
mem serve --mcp
```

### REST API

```bash
# Start server
mem serve --rest --port 8080

# Add a memory
curl -X POST http://localhost:8080/memories \
  -H "Content-Type: application/json" \
  -d '{"content": "Always use pytest", "category": "convention"}'

# Search
curl -X POST http://localhost:8080/memories/search \
  -H "Content-Type: application/json" \
  -d '{"query": "testing"}'
```

### MCP server

For multi-agent setups, Runtime Memory provides an MCP server:

```bash
mem serve --mcp
```

Configure in your MCP client:

```json
{
  "memory-layer": {
    "command": "mem",
    "args": ["serve", "--mcp"]
  }
}
```

#### Multi-agent configurations

All agents share the same memory store. Memories created in Claude Code appear in Cursor, feedback from OpenCode improves results everywhere.

**OpenCode** (`~/.opencode/config.json`):
```json
{
  "mcpServers": {
    "memory-layer": {
      "command": "mem",
      "args": ["serve", "--mcp"]
    }
  }
}
```

**Cursor** (`~/.cursor/mcp.json`):
```json
{
  "mcpServers": {
    "memory-layer": {
      "command": "mem",
      "args": ["serve", "--mcp"]
    }
  }
}
```

**Windsurf** (`~/.windsurf/mcp.json`):
```json
{
  "mcpServers": {
    "memory-layer": {
      "command": "mem",
      "args": ["serve", "--mcp"]
    }
  }
}
```

### Claude Code integration

Runtime Memory integrates with Claude Code via hooks and skills. For a beginner-friendly walkthrough, see the [User Guide](https://github.com/runtimenoteslabs/memory-layer/blob/main/USER_GUIDE.md).

**Installation:**

```bash
pip install "runtime-memory[all]"

# Go to your project directory
cd your-project

# Install Claude Code plugin
mem install-plugin

# Start Claude Code
claude
```

The `mem install-plugin` command creates:
- `.claude/settings.json` - Hooks for SessionStart, SessionEnd, PostToolUse
- `.claude/commands/` - Slash commands (/remember, /recall, /outcome, etc.)
- `.claude/skills/` - Agent skills (memory-retrieval, outcome-feedback, coding-patterns)
- `.claude-plugin/plugin.json` - Plugin manifest
- `.mcp.json` - MCP server configuration

**What happens automatically:**

- **SessionStart hook**: Loads relevant memories when you start Claude Code
- **PreCompact hook**: Extracts learnings before context compaction (prevents losing insights)
- **PostToolUse hook**: Tracks files you edit for context
- **SessionEnd hook**: Generates session summary when you exit
- **Skills**: Auto-retrieval when you ask "what's our convention...", feedback detection when you say "thanks, that worked!"

**Slash commands in Claude Code:**

```
/remember <content>              # Store a memory
/remember category:gotcha <content>  # Store with category
/recall <query>                  # Search memories
/memories                        # List all memories
/outcome <id> worked|failed      # Record feedback
/forget <id>                     # Archive a memory
/memory-context                  # Get project context
```

### Task integration (Beads and Claude Code)

Runtime Memory integrates with task trackers to automatically learn from task outcomes.

**Supported sources:**
- [Beads](https://github.com/steveyegge/beads) - `.beads/` directory
- Claude Code Tasks - `~/.claude/todos/` directory

**How it works:**
1. You work on a task, Claude searches for relevant memories
2. Those memories get linked to your task
3. When you mark the task done, the linked memories are recorded as having worked

```bash
# Unified task commands (all sources)
mem tasks                    # List all tasks
mem tasks --source beads     # Filter by source
mem tasks --source claude    # Claude Code tasks only
mem tasks-sync               # Sync outcomes
mem tasks-context            # Get task context with memories
mem tasks-stats              # View statistics

# Legacy Beads-specific commands (still supported)
mem beads-sync
mem beads-context
mem beads-stats
```

Runtime Memory finds `.beads/` and `~/.claude/todos/` itself; there is nothing to configure.

**Environment variables:**
- `CLAUDE_CODE_TASK_LIST_ID` - Filter to specific task list
- `CLAUDE_CODE_TODOS_DIR` - Custom todos directory location

### Hermes Agent integration

Runtime Memory can serve as Hermes Agent's memory provider, replacing its capped
note file with retrieval over the same store Claude Code and MCP clients use.

```bash
# Install into the environment Hermes runs in
~/.hermes/hermes-agent/venv/bin/python -m pip install "runtime-memory[embedding,extraction]"

hermes config set memory.provider runtimememory
```

Without the `embedding` extra in Hermes' own environment, the provider searches by
keyword only and logs a warning that names the interpreter.

Hermes finds the provider through the `hermes_agent.memory_providers` entry
point, so you do not edit its code or config files by hand. See
[docs/hermes.md](https://github.com/runtimenoteslabs/memory-layer/blob/main/docs/hermes.md) for configuration, the tool surface, and the
evaluation trace format.

### Web UI

Runtime Memory includes a web interface for browsing and managing memories.

```bash
# Start server with Web UI
mem serve --rest --port 8080

# Open http://localhost:8080
```

**Features:**
- Dashboard with category statistics
- Memory list with filtering and search
- Semantic and keyword search modes
- Task viewer (Beads + Claude Code)
- Add/edit memories
- Record outcomes
- Light/dark theme

## Memory categories

| Category | Use for | Example |
|----------|---------|---------|
| `architecture` | System design | "Microservices with event sourcing" |
| `convention` | Coding standards | "Use snake_case for Python" |
| `decision` | Technical choices | "Chose Postgres for ACID compliance" |
| `pattern` | Reusable solutions | "Repository pattern for data access" |
| `gotcha` | Pitfalls to avoid | "Don't use mutable default arguments" |
| `workaround` | Temporary fixes | "Redis reconnect hack for timeout bug" |
| `troubleshooting` | Error solutions | "Clear cache if tests fail randomly" |
| `command` | Useful commands | "npm run test:coverage" |
| `preference` | User preferences | "Prefer functional style" |

## Outcome scoring

| Outcome | Adds | When to use |
|---------|------|-------------|
| `worked` | one success | Advice solved the problem |
| `failed` | one failure | Advice was wrong or unhelpful |
| `partial` | a quarter of a success | Advice was on the right track |

A memory's outcome score is `(worked - 1.5 x failed) / (worked + 1.5 x failed + 2)`,
between -1 and 1. One success gives 0.33 and ten give 0.83, so a single
observation counts for less than a long record. A failure weighs 1.5 successes,
because following bad advice wastes debugging time. Each count halves every 90
days.

Retrieval leaves out a memory whose score is -0.5 or lower, which takes two
failures and no successes. One failure is not enough, because it may have been
blamed on the wrong memory. The memory is retrieved again once its failures
have faded.

To change these values, see `RetrievalConfig.outcome_model` and
`RetrievalConfig.failure_gate`.

## How retrieval works

Retrieval runs in two stages. Relevance to your query decides which memories
compete, then the other signals order them.

**Stage 1, the relevance pool.** A search keeps the `ceil(limit x 2)` memories
most relevant to the query and drops any with no relevance at all. Outcome records
reorder only the memories that match the query.

**Stage 2, the score.**

| Signal | Weight | Description |
|--------|--------|-------------|
| Semantic | 55% | Vector and keyword similarity to your query |
| Outcome | 25% | Learned effectiveness from feedback |
| Confidence | 10% | Extraction confidence score |
| Recency | 10% | Newer memories weighted higher (30-day half-life on age) |
| Frequency | 0% | Off by default; see below |

Outcome and confidence come from how memories have performed rather than from
the query, so ranking changes as feedback accumulates.

**What changed in 4.0.0, and why.** Tier 2 evaluation runs found the older
scoring deciding retrieval on signals that had nothing to do with the query:

- **Frequency left the default score.** It rewards having been retrieved, which
  is not evidence of having helped, and it compounds: a wrong memory held a top
  place through a whole task sequence on it. Set `frequency_weight` to bring it
  back.
- **Category boosts are neutral.** Multiplying the whole score by a category
  seated a memory that ranked about 25th on relevance at rank 1, and in another
  run kept the one memory that would have prevented a repeated mistake out of
  every prompt. Pass `category_boosts` to set your own.
- **Recency decays from a memory's age,** not from when it was last touched.
  Retrieval no longer moves that clock.

`RetrievalConfig.legacy_3x()` restores the 3.x weights, boosts and single-stage
scoring if you tuned for them.

### Category routing

`CategoryRouter` maps query wording to a category, but no search path calls it.
It is available to callers that want to pass `category=` themselves.

## Results

After 12 weeks of use:

| Metric | Improvement |
|--------|-------------|
| Retrieval precision | 70% → 90% |
| Session start context | 54% token savings |
| Post-compaction recovery | 84% token savings |
| Search latency (P95) | <150ms |

## Configuration

### Environment variables

| Variable | Description | Default |
|----------|-------------|---------|
| `ANTHROPIC_API_KEY` | For LLM-based extraction | Required for extraction features |
| `RUNTIME_MEMORY_DB` | Database location, read by the CLI, the MCP server and the Hermes provider | `~/.runtime-memory/memories.db` |
| `RUNTIME_MEMORY_ENV` | Environment (development/testing/production) | development |
| `RUNTIME_MEMORY_LOG_LEVEL` | Logging level | WARNING |
| `CLAUDE_CODE_TASK_LIST_ID` | Filter Claude Code tasks | None |
| `CLAUDE_CODE_TODOS_DIR` | Custom todos directory | `~/.claude/todos/` |

Variables set with the pre-3.0 prefix `MEMORY_LAYER_` are still read, under their
`RUNTIME_MEMORY_` names. The Hermes provider's own settings are in
[docs/hermes.md](https://github.com/runtimenoteslabs/memory-layer/blob/main/docs/hermes.md).

### Data location

```
~/.runtime-memory/
└── memories.db    # SQLite database
```

## Project structure

```
memory-layer/
├── src/runtime_memory/
│   ├── core/           # Storage, retrieval, models, config, resilience
│   ├── extraction/     # LLM-based memory extraction
│   ├── server/         # MCP server, REST API, Web UI
│   ├── tasks/          # Task integration (Beads, Claude Code)
│   ├── cli/            # Command-line interface
│   └── sdk/            # Python SDK
└── tests/
    ├── unit/
    ├── integration/
    └── ...
```

## Security

Runtime Memory is designed for local, single-user use:

- **Local storage**: All data stored in `~/.runtime-memory/` (SQLite database)
- **No external transmission**: Memories never leave your machine (except for LLM extraction if enabled)
- **Parameterized queries**: All database operations use parameterized SQL (no injection risk)
- **Input validation**: Pydantic models validate all API inputs
- **Server binding**: REST API binds to `127.0.0.1` by default (localhost only)

**API Keys**: If using LLM extraction features, set `ANTHROPIC_API_KEY` as an environment variable. Never commit API keys to version control.

**Multi-user warning**: The REST API and MCP server are not designed for multi-user/production deployment. For shared use, deploy behind an authentication proxy.

## Development

```bash
# Install dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Run linting
ruff check src tests
mypy src
```

## License

MIT

## Acknowledgments

Runtime Memory was inspired by studying 11 existing AI memory systems:

- [claude-mem](https://github.com/thedotmack/claude-mem) - UX patterns, progressive disclosure, web viewer
- [Claude Diary](https://github.com/rlancemartin/claude-diary) - Reflection synthesis, minimal viable memory
- [Mem0](https://github.com/mem0ai/mem0) - Hybrid storage patterns, community building
- [Graphiti/Zep](https://github.com/getzep/graphiti) - Bi-temporal modeling, research-grade benchmarks
- [CORE](https://github.com/RedPlanetHQ/core) - Knowledge graph architecture, temporal modeling
- [Supermemory](https://github.com/supermemoryai/supermemory) - Relationship types, temporal decay
- [Memvid](https://github.com/memvid/memvid) - Single-file portability, embedded WAL
- [Beads](https://github.com/steveyegge/beads) - Task integration, git-native tracking
- [Roampal](https://github.com/roampal-ai/roampal) - Independent validation of outcome-based learning

And thank you to Anthropic for CLAUDE.md - the right foundation for project memory.

The key insight: none of these systems learn from outcomes. Runtime Memory adds a feedback loop so memories that actually help rise to the top.
