runtime-memory 3.1.0__tar.gz → 4.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/PKG-INFO +101 -50
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/README.md +100 -49
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/__init__.py +1 -1
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/claude_code/commands.py +10 -7
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/claude_code/daemon.py +9 -9
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/claude_code/hooks.py +8 -8
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/cli/main.py +191 -3
- runtime_memory-4.0.0/src/runtime_memory/core/attribution.py +326 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/config.py +3 -3
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/embeddings.py +52 -20
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/engine.py +212 -9
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/models.py +26 -1
- runtime_memory-4.0.0/src/runtime_memory/core/outcomes.py +118 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/retrieval.py +319 -58
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/storage.py +261 -31
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/extraction/extractor.py +378 -30
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/hermes/provider.py +348 -52
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/hermes/tools.py +22 -6
- runtime_memory-4.0.0/src/runtime_memory/hermes/trace.py +342 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/tasks/adapter.py +18 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/tasks/claude_code_adapter.py +9 -2
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/tasks/unified_adapter.py +6 -2
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/integration/test_engine_integration.py +105 -6
- runtime_memory-4.0.0/tests/unit/test_attribution.py +185 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_claude_code_tasks.py +36 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_cli.py +102 -1
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_commands.py +27 -13
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_embeddings.py +34 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_engine.py +103 -5
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_extractor.py +382 -4
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_hermes_provider.py +580 -23
- runtime_memory-4.0.0/tests/unit/test_outcomes.py +136 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_retrieval.py +359 -32
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_storage.py +111 -0
- runtime_memory-3.1.0/src/runtime_memory/hermes/trace.py +0 -177
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/.gitignore +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/LICENSE +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/pyproject.toml +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/claude_code/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/cli/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/exceptions.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/legacy_env.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/logging.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/observability.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/paths.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/core/resilience.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/extraction/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/hermes/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/hermes/_base.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/hermes/bridge.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/hermes/plugin.yaml +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/plugin/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/sdk/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/sdk/client.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/server/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/server/api.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/server/mcp.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/server/static/css/styles.css +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/server/static/index.html +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/server/static/js/api.js +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/server/static/js/app.js +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/tasks/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/tasks/claude_code_parser.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/tasks/cli_bridge.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/tasks/linking.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/tasks/models.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/tasks/outcomes.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/src/runtime_memory/tasks/parser.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/conftest.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/functional/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/integration/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/integration/test_extractor_integration.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/performance/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/security/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/uat/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/uat/test_uat.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/__init__.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_api.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_beads.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_config.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_config_validation.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_daemon.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_exceptions.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_hooks.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_legacy_compat.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_mcp.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_models.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_observability.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_plugin.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_resilience.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_sdk.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_setup.py +0 -0
- {runtime_memory-3.1.0 → runtime_memory-4.0.0}/tests/unit/test_v2_models.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: runtime-memory
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: Persistent memory for AI coding agents with outcome-based learning
|
|
5
5
|
Project-URL: Homepage, https://github.com/runtimenoteslabs/memory-layer
|
|
6
6
|
Project-URL: Documentation, https://github.com/runtimenoteslabs/memory-layer#readme
|
|
@@ -66,20 +66,32 @@ Persistent memory for AI coding agents with outcome-based learning.
|
|
|
66
66
|
|
|
67
67
|
> **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.
|
|
68
68
|
|
|
69
|
-
## What
|
|
69
|
+
## What it does
|
|
70
70
|
|
|
71
|
-
Runtime Memory stores knowledge from your coding sessions and
|
|
71
|
+
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.
|
|
72
72
|
|
|
73
73
|
## Installation
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
|
-
pip install runtime-memory
|
|
76
|
+
pip install "runtime-memory[all]"
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
The base package stores and searches memories by keyword. Each extra adds a part:
|
|
80
|
+
|
|
81
|
+
| Extra | Adds | Without it |
|
|
82
|
+
|-------|------|------------|
|
|
83
|
+
| `embedding` | Semantic search, with `sentence-transformers` | Search is keyword-only |
|
|
84
|
+
| `extraction` | Extracting memories from sessions with Claude | No extraction |
|
|
85
|
+
| `server` | The MCP server, the REST API and the web UI | `mem serve` is unavailable |
|
|
86
|
+
| `all` | All three | |
|
|
87
|
+
|
|
88
|
+
`mem stats` shows which search mode is in use. With `embedding`, the first search
|
|
89
|
+
downloads an embedding model of about 100 MB and caches it.
|
|
90
|
+
|
|
79
91
|
Or from source:
|
|
80
92
|
|
|
81
93
|
```bash
|
|
82
|
-
pip install git+https://github.com/runtimenoteslabs/memory-layer.git
|
|
94
|
+
pip install "runtime-memory[all] @ git+https://github.com/runtimenoteslabs/memory-layer.git"
|
|
83
95
|
```
|
|
84
96
|
|
|
85
97
|
For development:
|
|
@@ -95,9 +107,7 @@ repository is still named memory-layer, which is where the project started; the
|
|
|
95
107
|
package was renamed in 3.0. An unrelated package holds `memory-layer` on PyPI,
|
|
96
108
|
so `pip install memory-layer` fetches that one instead of this project.
|
|
97
109
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
## Quick Start
|
|
110
|
+
## Quick start
|
|
101
111
|
|
|
102
112
|
### Python SDK
|
|
103
113
|
|
|
@@ -121,7 +131,7 @@ async with MemoryClient() as client:
|
|
|
121
131
|
context = await client.get_context()
|
|
122
132
|
```
|
|
123
133
|
|
|
124
|
-
### Synchronous
|
|
134
|
+
### Synchronous client
|
|
125
135
|
|
|
126
136
|
```python
|
|
127
137
|
from runtime_memory.sdk import SyncMemoryClient
|
|
@@ -140,6 +150,12 @@ mem add "Use type hints for better IDE support" -c convention
|
|
|
140
150
|
# Search memories
|
|
141
151
|
mem search "type hints"
|
|
142
152
|
|
|
153
|
+
# See why a search returns what it does, and why the rest were left out
|
|
154
|
+
mem why "type hints"
|
|
155
|
+
|
|
156
|
+
# Store statistics: outcome records, search mode, and the Hermes trace if present
|
|
157
|
+
mem stats
|
|
158
|
+
|
|
143
159
|
# Record outcome
|
|
144
160
|
mem outcome <memory-id> worked
|
|
145
161
|
|
|
@@ -170,7 +186,7 @@ curl -X POST http://localhost:8080/memories/search \
|
|
|
170
186
|
-d '{"query": "testing"}'
|
|
171
187
|
```
|
|
172
188
|
|
|
173
|
-
### MCP
|
|
189
|
+
### MCP server
|
|
174
190
|
|
|
175
191
|
For multi-agent setups, Runtime Memory provides an MCP server:
|
|
176
192
|
|
|
@@ -189,7 +205,7 @@ Configure in your MCP client:
|
|
|
189
205
|
}
|
|
190
206
|
```
|
|
191
207
|
|
|
192
|
-
#### Multi-
|
|
208
|
+
#### Multi-agent configurations
|
|
193
209
|
|
|
194
210
|
All agents share the same memory store. Memories created in Claude Code appear in Cursor, feedback from OpenCode improves results everywhere.
|
|
195
211
|
|
|
@@ -229,14 +245,14 @@ All agents share the same memory store. Memories created in Claude Code appear i
|
|
|
229
245
|
}
|
|
230
246
|
```
|
|
231
247
|
|
|
232
|
-
### Claude Code
|
|
248
|
+
### Claude Code integration
|
|
233
249
|
|
|
234
250
|
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).
|
|
235
251
|
|
|
236
252
|
**Installation:**
|
|
237
253
|
|
|
238
254
|
```bash
|
|
239
|
-
pip install runtime-memory
|
|
255
|
+
pip install "runtime-memory[all]"
|
|
240
256
|
|
|
241
257
|
# Go to your project directory
|
|
242
258
|
cd your-project
|
|
@@ -275,7 +291,7 @@ The `mem install-plugin` command creates:
|
|
|
275
291
|
/memory-context # Get project context
|
|
276
292
|
```
|
|
277
293
|
|
|
278
|
-
### Task
|
|
294
|
+
### Task integration (Beads and Claude Code)
|
|
279
295
|
|
|
280
296
|
Runtime Memory integrates with task trackers to automatically learn from task outcomes.
|
|
281
297
|
|
|
@@ -286,7 +302,7 @@ Runtime Memory integrates with task trackers to automatically learn from task ou
|
|
|
286
302
|
**How it works:**
|
|
287
303
|
1. You work on a task, Claude searches for relevant memories
|
|
288
304
|
2. Those memories get linked to your task
|
|
289
|
-
3. When you mark the task done, linked memories are
|
|
305
|
+
3. When you mark the task done, the linked memories are recorded as having worked
|
|
290
306
|
|
|
291
307
|
```bash
|
|
292
308
|
# Unified task commands (all sources)
|
|
@@ -303,25 +319,27 @@ mem beads-context
|
|
|
303
319
|
mem beads-stats
|
|
304
320
|
```
|
|
305
321
|
|
|
306
|
-
|
|
322
|
+
Runtime Memory finds `.beads/` and `~/.claude/todos/` itself; there is nothing to configure.
|
|
307
323
|
|
|
308
324
|
**Environment variables:**
|
|
309
325
|
- `CLAUDE_CODE_TASK_LIST_ID` - Filter to specific task list
|
|
310
326
|
- `CLAUDE_CODE_TODOS_DIR` - Custom todos directory location
|
|
311
327
|
|
|
312
|
-
### Hermes Agent
|
|
328
|
+
### Hermes Agent integration
|
|
313
329
|
|
|
314
330
|
Runtime Memory can serve as Hermes Agent's memory provider, replacing its capped
|
|
315
331
|
note file with retrieval over the same store Claude Code and MCP clients use.
|
|
316
332
|
|
|
317
333
|
```bash
|
|
318
334
|
# Install into the environment Hermes runs in
|
|
319
|
-
~/.hermes/hermes-agent/venv/bin/python -m pip install
|
|
320
|
-
git+https://github.com/runtimenoteslabs/memory-layer.git
|
|
335
|
+
~/.hermes/hermes-agent/venv/bin/python -m pip install "runtime-memory[embedding,extraction]"
|
|
321
336
|
|
|
322
337
|
hermes config set memory.provider runtimememory
|
|
323
338
|
```
|
|
324
339
|
|
|
340
|
+
Without the `embedding` extra in Hermes' own environment, the provider searches by
|
|
341
|
+
keyword only and logs a warning that names the interpreter.
|
|
342
|
+
|
|
325
343
|
Hermes finds the provider through the `hermes_agent.memory_providers` entry
|
|
326
344
|
point, so you do not edit its code or config files by hand. See
|
|
327
345
|
[docs/hermes.md](https://github.com/runtimenoteslabs/memory-layer/blob/main/docs/hermes.md) for configuration, the tool surface, and the
|
|
@@ -347,9 +365,9 @@ mem serve --rest --port 8080
|
|
|
347
365
|
- Record outcomes
|
|
348
366
|
- Light/dark theme
|
|
349
367
|
|
|
350
|
-
## Memory
|
|
368
|
+
## Memory categories
|
|
351
369
|
|
|
352
|
-
| Category | Use
|
|
370
|
+
| Category | Use for | Example |
|
|
353
371
|
|----------|---------|---------|
|
|
354
372
|
| `architecture` | System design | "Microservices with event sourcing" |
|
|
355
373
|
| `convention` | Coding standards | "Use snake_case for Python" |
|
|
@@ -361,42 +379,71 @@ mem serve --rest --port 8080
|
|
|
361
379
|
| `command` | Useful commands | "npm run test:coverage" |
|
|
362
380
|
| `preference` | User preferences | "Prefer functional style" |
|
|
363
381
|
|
|
364
|
-
## Outcome
|
|
382
|
+
## Outcome scoring
|
|
365
383
|
|
|
366
|
-
| Outcome |
|
|
367
|
-
|
|
368
|
-
| `worked` |
|
|
369
|
-
| `failed` |
|
|
370
|
-
| `partial` |
|
|
384
|
+
| Outcome | Adds | When to use |
|
|
385
|
+
|---------|------|-------------|
|
|
386
|
+
| `worked` | one success | Advice solved the problem |
|
|
387
|
+
| `failed` | one failure | Advice was wrong or unhelpful |
|
|
388
|
+
| `partial` | a quarter of a success | Advice was on the right track |
|
|
371
389
|
|
|
372
|
-
|
|
390
|
+
A memory's outcome score is `(worked - 1.5 x failed) / (worked + 1.5 x failed + 2)`,
|
|
391
|
+
between -1 and 1. One success gives 0.33 and ten give 0.83, so a single
|
|
392
|
+
observation counts for less than a long record. A failure weighs 1.5 successes,
|
|
393
|
+
because following bad advice wastes debugging time. Each count halves every 90
|
|
394
|
+
days.
|
|
373
395
|
|
|
374
|
-
|
|
396
|
+
Retrieval leaves out a memory whose score is -0.5 or lower, which takes two
|
|
397
|
+
failures and no successes. One failure is not enough, because it may have been
|
|
398
|
+
blamed on the wrong memory. The memory is retrieved again once its failures
|
|
399
|
+
have faded.
|
|
375
400
|
|
|
376
|
-
|
|
401
|
+
To change these values, see `RetrievalConfig.outcome_model` and
|
|
402
|
+
`RetrievalConfig.failure_gate`.
|
|
403
|
+
|
|
404
|
+
## How retrieval works
|
|
405
|
+
|
|
406
|
+
Retrieval runs in two stages. Relevance to your query decides which memories
|
|
407
|
+
compete, then the other signals order them.
|
|
408
|
+
|
|
409
|
+
**Stage 1, the relevance pool.** A search keeps the `ceil(limit x 2)` memories
|
|
410
|
+
most relevant to the query and drops any with no relevance at all. Outcome records
|
|
411
|
+
reorder only the memories that match the query.
|
|
412
|
+
|
|
413
|
+
**Stage 2, the score.**
|
|
377
414
|
|
|
378
415
|
| Signal | Weight | Description |
|
|
379
416
|
|--------|--------|-------------|
|
|
380
|
-
| Semantic |
|
|
417
|
+
| Semantic | 55% | Vector and keyword similarity to your query |
|
|
381
418
|
| Outcome | 25% | Learned effectiveness from feedback |
|
|
382
|
-
| Recency | 15% | Recent memories weighted higher (30-day half-life) |
|
|
383
|
-
| Frequency | 15% | Frequently used memories rise |
|
|
384
419
|
| Confidence | 10% | Extraction confidence score |
|
|
420
|
+
| Recency | 10% | Newer memories weighted higher (30-day half-life on age) |
|
|
421
|
+
| Frequency | 0% | Off by default; see below |
|
|
385
422
|
|
|
386
|
-
|
|
387
|
-
|
|
423
|
+
Outcome and confidence come from how memories have performed rather than from
|
|
424
|
+
the query, so ranking changes as feedback accumulates.
|
|
388
425
|
|
|
389
|
-
|
|
426
|
+
**What changed in 4.0.0, and why.** Tier 2 evaluation runs found the older
|
|
427
|
+
scoring deciding retrieval on signals that had nothing to do with the query:
|
|
390
428
|
|
|
391
|
-
|
|
429
|
+
- **Frequency left the default score.** It rewards having been retrieved, which
|
|
430
|
+
is not evidence of having helped, and it compounds: a wrong memory held a top
|
|
431
|
+
place through a whole task sequence on it. Set `frequency_weight` to bring it
|
|
432
|
+
back.
|
|
433
|
+
- **Category boosts are neutral.** Multiplying the whole score by a category
|
|
434
|
+
seated a memory that ranked about 25th on relevance at rank 1, and in another
|
|
435
|
+
run kept the one memory that would have prevented a repeated mistake out of
|
|
436
|
+
every prompt. Pass `category_boosts` to set your own.
|
|
437
|
+
- **Recency decays from a memory's age,** not from when it was last touched.
|
|
438
|
+
Retrieval no longer moves that clock.
|
|
392
439
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
440
|
+
`RetrievalConfig.legacy_3x()` restores the 3.x weights, boosts and single-stage
|
|
441
|
+
scoring if you tuned for them.
|
|
442
|
+
|
|
443
|
+
### Category routing
|
|
444
|
+
|
|
445
|
+
`CategoryRouter` maps query wording to a category, but no search path calls it.
|
|
446
|
+
It is available to callers that want to pass `category=` themselves.
|
|
400
447
|
|
|
401
448
|
## Results
|
|
402
449
|
|
|
@@ -411,25 +458,29 @@ After 12 weeks of use:
|
|
|
411
458
|
|
|
412
459
|
## Configuration
|
|
413
460
|
|
|
414
|
-
### Environment
|
|
461
|
+
### Environment variables
|
|
415
462
|
|
|
416
463
|
| Variable | Description | Default |
|
|
417
464
|
|----------|-------------|---------|
|
|
418
465
|
| `ANTHROPIC_API_KEY` | For LLM-based extraction | Required for extraction features |
|
|
419
|
-
| `
|
|
420
|
-
| `
|
|
421
|
-
| `
|
|
466
|
+
| `RUNTIME_MEMORY_DB` | Database location, read by the CLI, the MCP server and the Hermes provider | `~/.runtime-memory/memories.db` |
|
|
467
|
+
| `RUNTIME_MEMORY_ENV` | Environment (development/testing/production) | development |
|
|
468
|
+
| `RUNTIME_MEMORY_LOG_LEVEL` | Logging level | WARNING |
|
|
422
469
|
| `CLAUDE_CODE_TASK_LIST_ID` | Filter Claude Code tasks | None |
|
|
423
470
|
| `CLAUDE_CODE_TODOS_DIR` | Custom todos directory | `~/.claude/todos/` |
|
|
424
471
|
|
|
425
|
-
|
|
472
|
+
Variables set with the pre-3.0 prefix `MEMORY_LAYER_` are still read, under their
|
|
473
|
+
`RUNTIME_MEMORY_` names. The Hermes provider's own settings are in
|
|
474
|
+
[docs/hermes.md](https://github.com/runtimenoteslabs/memory-layer/blob/main/docs/hermes.md).
|
|
475
|
+
|
|
476
|
+
### Data location
|
|
426
477
|
|
|
427
478
|
```
|
|
428
479
|
~/.runtime-memory/
|
|
429
480
|
└── memories.db # SQLite database
|
|
430
481
|
```
|
|
431
482
|
|
|
432
|
-
## Project
|
|
483
|
+
## Project structure
|
|
433
484
|
|
|
434
485
|
```
|
|
435
486
|
memory-layer/
|
|
@@ -4,20 +4,32 @@ Persistent memory for AI coding agents with outcome-based learning.
|
|
|
4
4
|
|
|
5
5
|
> **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.
|
|
6
6
|
|
|
7
|
-
## What
|
|
7
|
+
## What it does
|
|
8
8
|
|
|
9
|
-
Runtime Memory stores knowledge from your coding sessions and
|
|
9
|
+
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.
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
pip install runtime-memory
|
|
14
|
+
pip install "runtime-memory[all]"
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
The base package stores and searches memories by keyword. Each extra adds a part:
|
|
18
|
+
|
|
19
|
+
| Extra | Adds | Without it |
|
|
20
|
+
|-------|------|------------|
|
|
21
|
+
| `embedding` | Semantic search, with `sentence-transformers` | Search is keyword-only |
|
|
22
|
+
| `extraction` | Extracting memories from sessions with Claude | No extraction |
|
|
23
|
+
| `server` | The MCP server, the REST API and the web UI | `mem serve` is unavailable |
|
|
24
|
+
| `all` | All three | |
|
|
25
|
+
|
|
26
|
+
`mem stats` shows which search mode is in use. With `embedding`, the first search
|
|
27
|
+
downloads an embedding model of about 100 MB and caches it.
|
|
28
|
+
|
|
17
29
|
Or from source:
|
|
18
30
|
|
|
19
31
|
```bash
|
|
20
|
-
pip install git+https://github.com/runtimenoteslabs/memory-layer.git
|
|
32
|
+
pip install "runtime-memory[all] @ git+https://github.com/runtimenoteslabs/memory-layer.git"
|
|
21
33
|
```
|
|
22
34
|
|
|
23
35
|
For development:
|
|
@@ -33,9 +45,7 @@ repository is still named memory-layer, which is where the project started; the
|
|
|
33
45
|
package was renamed in 3.0. An unrelated package holds `memory-layer` on PyPI,
|
|
34
46
|
so `pip install memory-layer` fetches that one instead of this project.
|
|
35
47
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
## Quick Start
|
|
48
|
+
## Quick start
|
|
39
49
|
|
|
40
50
|
### Python SDK
|
|
41
51
|
|
|
@@ -59,7 +69,7 @@ async with MemoryClient() as client:
|
|
|
59
69
|
context = await client.get_context()
|
|
60
70
|
```
|
|
61
71
|
|
|
62
|
-
### Synchronous
|
|
72
|
+
### Synchronous client
|
|
63
73
|
|
|
64
74
|
```python
|
|
65
75
|
from runtime_memory.sdk import SyncMemoryClient
|
|
@@ -78,6 +88,12 @@ mem add "Use type hints for better IDE support" -c convention
|
|
|
78
88
|
# Search memories
|
|
79
89
|
mem search "type hints"
|
|
80
90
|
|
|
91
|
+
# See why a search returns what it does, and why the rest were left out
|
|
92
|
+
mem why "type hints"
|
|
93
|
+
|
|
94
|
+
# Store statistics: outcome records, search mode, and the Hermes trace if present
|
|
95
|
+
mem stats
|
|
96
|
+
|
|
81
97
|
# Record outcome
|
|
82
98
|
mem outcome <memory-id> worked
|
|
83
99
|
|
|
@@ -108,7 +124,7 @@ curl -X POST http://localhost:8080/memories/search \
|
|
|
108
124
|
-d '{"query": "testing"}'
|
|
109
125
|
```
|
|
110
126
|
|
|
111
|
-
### MCP
|
|
127
|
+
### MCP server
|
|
112
128
|
|
|
113
129
|
For multi-agent setups, Runtime Memory provides an MCP server:
|
|
114
130
|
|
|
@@ -127,7 +143,7 @@ Configure in your MCP client:
|
|
|
127
143
|
}
|
|
128
144
|
```
|
|
129
145
|
|
|
130
|
-
#### Multi-
|
|
146
|
+
#### Multi-agent configurations
|
|
131
147
|
|
|
132
148
|
All agents share the same memory store. Memories created in Claude Code appear in Cursor, feedback from OpenCode improves results everywhere.
|
|
133
149
|
|
|
@@ -167,14 +183,14 @@ All agents share the same memory store. Memories created in Claude Code appear i
|
|
|
167
183
|
}
|
|
168
184
|
```
|
|
169
185
|
|
|
170
|
-
### Claude Code
|
|
186
|
+
### Claude Code integration
|
|
171
187
|
|
|
172
188
|
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).
|
|
173
189
|
|
|
174
190
|
**Installation:**
|
|
175
191
|
|
|
176
192
|
```bash
|
|
177
|
-
pip install runtime-memory
|
|
193
|
+
pip install "runtime-memory[all]"
|
|
178
194
|
|
|
179
195
|
# Go to your project directory
|
|
180
196
|
cd your-project
|
|
@@ -213,7 +229,7 @@ The `mem install-plugin` command creates:
|
|
|
213
229
|
/memory-context # Get project context
|
|
214
230
|
```
|
|
215
231
|
|
|
216
|
-
### Task
|
|
232
|
+
### Task integration (Beads and Claude Code)
|
|
217
233
|
|
|
218
234
|
Runtime Memory integrates with task trackers to automatically learn from task outcomes.
|
|
219
235
|
|
|
@@ -224,7 +240,7 @@ Runtime Memory integrates with task trackers to automatically learn from task ou
|
|
|
224
240
|
**How it works:**
|
|
225
241
|
1. You work on a task, Claude searches for relevant memories
|
|
226
242
|
2. Those memories get linked to your task
|
|
227
|
-
3. When you mark the task done, linked memories are
|
|
243
|
+
3. When you mark the task done, the linked memories are recorded as having worked
|
|
228
244
|
|
|
229
245
|
```bash
|
|
230
246
|
# Unified task commands (all sources)
|
|
@@ -241,25 +257,27 @@ mem beads-context
|
|
|
241
257
|
mem beads-stats
|
|
242
258
|
```
|
|
243
259
|
|
|
244
|
-
|
|
260
|
+
Runtime Memory finds `.beads/` and `~/.claude/todos/` itself; there is nothing to configure.
|
|
245
261
|
|
|
246
262
|
**Environment variables:**
|
|
247
263
|
- `CLAUDE_CODE_TASK_LIST_ID` - Filter to specific task list
|
|
248
264
|
- `CLAUDE_CODE_TODOS_DIR` - Custom todos directory location
|
|
249
265
|
|
|
250
|
-
### Hermes Agent
|
|
266
|
+
### Hermes Agent integration
|
|
251
267
|
|
|
252
268
|
Runtime Memory can serve as Hermes Agent's memory provider, replacing its capped
|
|
253
269
|
note file with retrieval over the same store Claude Code and MCP clients use.
|
|
254
270
|
|
|
255
271
|
```bash
|
|
256
272
|
# Install into the environment Hermes runs in
|
|
257
|
-
~/.hermes/hermes-agent/venv/bin/python -m pip install
|
|
258
|
-
git+https://github.com/runtimenoteslabs/memory-layer.git
|
|
273
|
+
~/.hermes/hermes-agent/venv/bin/python -m pip install "runtime-memory[embedding,extraction]"
|
|
259
274
|
|
|
260
275
|
hermes config set memory.provider runtimememory
|
|
261
276
|
```
|
|
262
277
|
|
|
278
|
+
Without the `embedding` extra in Hermes' own environment, the provider searches by
|
|
279
|
+
keyword only and logs a warning that names the interpreter.
|
|
280
|
+
|
|
263
281
|
Hermes finds the provider through the `hermes_agent.memory_providers` entry
|
|
264
282
|
point, so you do not edit its code or config files by hand. See
|
|
265
283
|
[docs/hermes.md](https://github.com/runtimenoteslabs/memory-layer/blob/main/docs/hermes.md) for configuration, the tool surface, and the
|
|
@@ -285,9 +303,9 @@ mem serve --rest --port 8080
|
|
|
285
303
|
- Record outcomes
|
|
286
304
|
- Light/dark theme
|
|
287
305
|
|
|
288
|
-
## Memory
|
|
306
|
+
## Memory categories
|
|
289
307
|
|
|
290
|
-
| Category | Use
|
|
308
|
+
| Category | Use for | Example |
|
|
291
309
|
|----------|---------|---------|
|
|
292
310
|
| `architecture` | System design | "Microservices with event sourcing" |
|
|
293
311
|
| `convention` | Coding standards | "Use snake_case for Python" |
|
|
@@ -299,42 +317,71 @@ mem serve --rest --port 8080
|
|
|
299
317
|
| `command` | Useful commands | "npm run test:coverage" |
|
|
300
318
|
| `preference` | User preferences | "Prefer functional style" |
|
|
301
319
|
|
|
302
|
-
## Outcome
|
|
320
|
+
## Outcome scoring
|
|
303
321
|
|
|
304
|
-
| Outcome |
|
|
305
|
-
|
|
306
|
-
| `worked` |
|
|
307
|
-
| `failed` |
|
|
308
|
-
| `partial` |
|
|
322
|
+
| Outcome | Adds | When to use |
|
|
323
|
+
|---------|------|-------------|
|
|
324
|
+
| `worked` | one success | Advice solved the problem |
|
|
325
|
+
| `failed` | one failure | Advice was wrong or unhelpful |
|
|
326
|
+
| `partial` | a quarter of a success | Advice was on the right track |
|
|
309
327
|
|
|
310
|
-
|
|
328
|
+
A memory's outcome score is `(worked - 1.5 x failed) / (worked + 1.5 x failed + 2)`,
|
|
329
|
+
between -1 and 1. One success gives 0.33 and ten give 0.83, so a single
|
|
330
|
+
observation counts for less than a long record. A failure weighs 1.5 successes,
|
|
331
|
+
because following bad advice wastes debugging time. Each count halves every 90
|
|
332
|
+
days.
|
|
311
333
|
|
|
312
|
-
|
|
334
|
+
Retrieval leaves out a memory whose score is -0.5 or lower, which takes two
|
|
335
|
+
failures and no successes. One failure is not enough, because it may have been
|
|
336
|
+
blamed on the wrong memory. The memory is retrieved again once its failures
|
|
337
|
+
have faded.
|
|
313
338
|
|
|
314
|
-
|
|
339
|
+
To change these values, see `RetrievalConfig.outcome_model` and
|
|
340
|
+
`RetrievalConfig.failure_gate`.
|
|
341
|
+
|
|
342
|
+
## How retrieval works
|
|
343
|
+
|
|
344
|
+
Retrieval runs in two stages. Relevance to your query decides which memories
|
|
345
|
+
compete, then the other signals order them.
|
|
346
|
+
|
|
347
|
+
**Stage 1, the relevance pool.** A search keeps the `ceil(limit x 2)` memories
|
|
348
|
+
most relevant to the query and drops any with no relevance at all. Outcome records
|
|
349
|
+
reorder only the memories that match the query.
|
|
350
|
+
|
|
351
|
+
**Stage 2, the score.**
|
|
315
352
|
|
|
316
353
|
| Signal | Weight | Description |
|
|
317
354
|
|--------|--------|-------------|
|
|
318
|
-
| Semantic |
|
|
355
|
+
| Semantic | 55% | Vector and keyword similarity to your query |
|
|
319
356
|
| Outcome | 25% | Learned effectiveness from feedback |
|
|
320
|
-
| Recency | 15% | Recent memories weighted higher (30-day half-life) |
|
|
321
|
-
| Frequency | 15% | Frequently used memories rise |
|
|
322
357
|
| Confidence | 10% | Extraction confidence score |
|
|
358
|
+
| Recency | 10% | Newer memories weighted higher (30-day half-life on age) |
|
|
359
|
+
| Frequency | 0% | Off by default; see below |
|
|
323
360
|
|
|
324
|
-
|
|
325
|
-
|
|
361
|
+
Outcome and confidence come from how memories have performed rather than from
|
|
362
|
+
the query, so ranking changes as feedback accumulates.
|
|
326
363
|
|
|
327
|
-
|
|
364
|
+
**What changed in 4.0.0, and why.** Tier 2 evaluation runs found the older
|
|
365
|
+
scoring deciding retrieval on signals that had nothing to do with the query:
|
|
328
366
|
|
|
329
|
-
|
|
367
|
+
- **Frequency left the default score.** It rewards having been retrieved, which
|
|
368
|
+
is not evidence of having helped, and it compounds: a wrong memory held a top
|
|
369
|
+
place through a whole task sequence on it. Set `frequency_weight` to bring it
|
|
370
|
+
back.
|
|
371
|
+
- **Category boosts are neutral.** Multiplying the whole score by a category
|
|
372
|
+
seated a memory that ranked about 25th on relevance at rank 1, and in another
|
|
373
|
+
run kept the one memory that would have prevented a repeated mistake out of
|
|
374
|
+
every prompt. Pass `category_boosts` to set your own.
|
|
375
|
+
- **Recency decays from a memory's age,** not from when it was last touched.
|
|
376
|
+
Retrieval no longer moves that clock.
|
|
330
377
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
378
|
+
`RetrievalConfig.legacy_3x()` restores the 3.x weights, boosts and single-stage
|
|
379
|
+
scoring if you tuned for them.
|
|
380
|
+
|
|
381
|
+
### Category routing
|
|
382
|
+
|
|
383
|
+
`CategoryRouter` maps query wording to a category, but no search path calls it.
|
|
384
|
+
It is available to callers that want to pass `category=` themselves.
|
|
338
385
|
|
|
339
386
|
## Results
|
|
340
387
|
|
|
@@ -349,25 +396,29 @@ After 12 weeks of use:
|
|
|
349
396
|
|
|
350
397
|
## Configuration
|
|
351
398
|
|
|
352
|
-
### Environment
|
|
399
|
+
### Environment variables
|
|
353
400
|
|
|
354
401
|
| Variable | Description | Default |
|
|
355
402
|
|----------|-------------|---------|
|
|
356
403
|
| `ANTHROPIC_API_KEY` | For LLM-based extraction | Required for extraction features |
|
|
357
|
-
| `
|
|
358
|
-
| `
|
|
359
|
-
| `
|
|
404
|
+
| `RUNTIME_MEMORY_DB` | Database location, read by the CLI, the MCP server and the Hermes provider | `~/.runtime-memory/memories.db` |
|
|
405
|
+
| `RUNTIME_MEMORY_ENV` | Environment (development/testing/production) | development |
|
|
406
|
+
| `RUNTIME_MEMORY_LOG_LEVEL` | Logging level | WARNING |
|
|
360
407
|
| `CLAUDE_CODE_TASK_LIST_ID` | Filter Claude Code tasks | None |
|
|
361
408
|
| `CLAUDE_CODE_TODOS_DIR` | Custom todos directory | `~/.claude/todos/` |
|
|
362
409
|
|
|
363
|
-
|
|
410
|
+
Variables set with the pre-3.0 prefix `MEMORY_LAYER_` are still read, under their
|
|
411
|
+
`RUNTIME_MEMORY_` names. The Hermes provider's own settings are in
|
|
412
|
+
[docs/hermes.md](https://github.com/runtimenoteslabs/memory-layer/blob/main/docs/hermes.md).
|
|
413
|
+
|
|
414
|
+
### Data location
|
|
364
415
|
|
|
365
416
|
```
|
|
366
417
|
~/.runtime-memory/
|
|
367
418
|
└── memories.db # SQLite database
|
|
368
419
|
```
|
|
369
420
|
|
|
370
|
-
## Project
|
|
421
|
+
## Project structure
|
|
371
422
|
|
|
372
423
|
```
|
|
373
424
|
memory-layer/
|
|
@@ -373,13 +373,16 @@ class CommandHandler:
|
|
|
373
373
|
error=f"No memory found with ID: {memory_id}",
|
|
374
374
|
)
|
|
375
375
|
|
|
376
|
-
# Record the outcome
|
|
376
|
+
# Record the outcome. The engine stores no notes, so they are only echoed back.
|
|
377
377
|
old_score = memory.outcome_score
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
378
|
+
updated = await self.engine.record_outcome([memory_id], outcome)
|
|
379
|
+
if not updated:
|
|
380
|
+
return CommandResult(
|
|
381
|
+
success=False,
|
|
382
|
+
command=CommandType.OUTCOME,
|
|
383
|
+
message="Memory not found",
|
|
384
|
+
error=f"No memory found with ID: {memory_id}",
|
|
385
|
+
)
|
|
383
386
|
|
|
384
387
|
return CommandResult(
|
|
385
388
|
success=True,
|
|
@@ -389,7 +392,7 @@ class CommandHandler:
|
|
|
389
392
|
"memory_id": memory_id,
|
|
390
393
|
"outcome": outcome.value,
|
|
391
394
|
"old_score": old_score,
|
|
392
|
-
"new_score":
|
|
395
|
+
"new_score": updated[0].outcome_score,
|
|
393
396
|
"notes": notes,
|
|
394
397
|
},
|
|
395
398
|
)
|