loop-memory 0.4.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.
- loop_memory-0.4.0/LICENSE +21 -0
- loop_memory-0.4.0/PKG-INFO +627 -0
- loop_memory-0.4.0/README.md +583 -0
- loop_memory-0.4.0/loop_memory/__init__.py +62 -0
- loop_memory-0.4.0/loop_memory/backends/__init__.py +13 -0
- loop_memory-0.4.0/loop_memory/backends/embedding.py +82 -0
- loop_memory-0.4.0/loop_memory/backends/sentence_embedder.py +30 -0
- loop_memory-0.4.0/loop_memory/backends/vector_store.py +139 -0
- loop_memory-0.4.0/loop_memory/cli/__init__.py +0 -0
- loop_memory-0.4.0/loop_memory/cli/_common.py +68 -0
- loop_memory-0.4.0/loop_memory/cli/commands/__init__.py +13 -0
- loop_memory-0.4.0/loop_memory/cli/commands/cognitive.py +205 -0
- loop_memory-0.4.0/loop_memory/cli/commands/diag.py +346 -0
- loop_memory-0.4.0/loop_memory/cli/commands/graph.py +21 -0
- loop_memory-0.4.0/loop_memory/cli/commands/hooks.py +212 -0
- loop_memory-0.4.0/loop_memory/cli/commands/read.py +362 -0
- loop_memory-0.4.0/loop_memory/cli/commands/serve.py +147 -0
- loop_memory-0.4.0/loop_memory/cli/commands/write.py +138 -0
- loop_memory-0.4.0/loop_memory/cli/main.py +115 -0
- loop_memory-0.4.0/loop_memory/engine/__init__.py +0 -0
- loop_memory-0.4.0/loop_memory/engine/loop.py +247 -0
- loop_memory-0.4.0/loop_memory/engine/reflect.py +89 -0
- loop_memory-0.4.0/loop_memory/examples/__init__.py +0 -0
- loop_memory-0.4.0/loop_memory/examples/demo.py +39 -0
- loop_memory-0.4.0/loop_memory/export/__init__.py +39 -0
- loop_memory-0.4.0/loop_memory/export/memory_md.py +629 -0
- loop_memory-0.4.0/loop_memory/graph/__init__.py +0 -0
- loop_memory-0.4.0/loop_memory/graph/build.py +259 -0
- loop_memory-0.4.0/loop_memory/graph/extract.py +197 -0
- loop_memory-0.4.0/loop_memory/ingest/__init__.py +0 -0
- loop_memory-0.4.0/loop_memory/ingest/loader.py +782 -0
- loop_memory-0.4.0/loop_memory/ingest/pipeline.py +458 -0
- loop_memory-0.4.0/loop_memory/jobs/__init__.py +0 -0
- loop_memory-0.4.0/loop_memory/jobs/cognitive.py +353 -0
- loop_memory-0.4.0/loop_memory/jobs/compact.py +371 -0
- loop_memory-0.4.0/loop_memory/jobs/consolidate.py +95 -0
- loop_memory-0.4.0/loop_memory/jobs/contradiction.py +281 -0
- loop_memory-0.4.0/loop_memory/jobs/evolution.py +2021 -0
- loop_memory-0.4.0/loop_memory/jobs/graph.py +395 -0
- loop_memory-0.4.0/loop_memory/jobs/llm_compact_pass.py +24 -0
- loop_memory-0.4.0/loop_memory/jobs/llm_consolidate.py +980 -0
- loop_memory-0.4.0/loop_memory/jobs/scheduler.py +495 -0
- loop_memory-0.4.0/loop_memory/llm/__init__.py +0 -0
- loop_memory-0.4.0/loop_memory/llm/base.py +80 -0
- loop_memory-0.4.0/loop_memory/llm/openai_adapter.py +31 -0
- loop_memory-0.4.0/loop_memory/llm/providers.py +517 -0
- loop_memory-0.4.0/loop_memory/mcp/__init__.py +804 -0
- loop_memory-0.4.0/loop_memory/memory/__init__.py +0 -0
- loop_memory-0.4.0/loop_memory/memory/types.py +199 -0
- loop_memory-0.4.0/loop_memory/privacy/__init__.py +22 -0
- loop_memory-0.4.0/loop_memory/privacy/private.py +46 -0
- loop_memory-0.4.0/loop_memory/privacy/redact.py +188 -0
- loop_memory-0.4.0/loop_memory/py.typed +0 -0
- loop_memory-0.4.0/loop_memory/sdk.py +875 -0
- loop_memory-0.4.0/loop_memory/sdk_extensions.py +384 -0
- loop_memory-0.4.0/loop_memory/security/__init__.py +20 -0
- loop_memory-0.4.0/loop_memory/security/secrets.py +464 -0
- loop_memory-0.4.0/loop_memory/serve/__init__.py +0 -0
- loop_memory-0.4.0/loop_memory/serve/app.py +506 -0
- loop_memory-0.4.0/loop_memory/serve/handlers.py +316 -0
- loop_memory-0.4.0/loop_memory/serve/routes/_shared.py +59 -0
- loop_memory-0.4.0/loop_memory/serve/routes/admin.py +970 -0
- loop_memory-0.4.0/loop_memory/serve/routes/cognitive.py +64 -0
- loop_memory-0.4.0/loop_memory/serve/routes/export.py +65 -0
- loop_memory-0.4.0/loop_memory/serve/routes/graph.py +101 -0
- loop_memory-0.4.0/loop_memory/serve/routes/insights.py +702 -0
- loop_memory-0.4.0/loop_memory/serve/routes/memories.py +435 -0
- loop_memory-0.4.0/loop_memory/serve/routes/sessions.py +75 -0
- loop_memory-0.4.0/loop_memory/serve/routes/system.py +493 -0
- loop_memory-0.4.0/loop_memory/serve/routes/wiki.py +812 -0
- loop_memory-0.4.0/loop_memory/serve/static/__init__.py +0 -0
- loop_memory-0.4.0/loop_memory/serve/static/index.html +15 -0
- loop_memory-0.4.0/loop_memory/serve/watcher.py +451 -0
- loop_memory-0.4.0/loop_memory/storage/__init__.py +5 -0
- loop_memory-0.4.0/loop_memory/storage/retrieval.py +365 -0
- loop_memory-0.4.0/loop_memory/storage/sqlite_store.py +3627 -0
- loop_memory-0.4.0/loop_memory/wiki/__init__.py +41 -0
- loop_memory-0.4.0/loop_memory/wiki/backfill.py +143 -0
- loop_memory-0.4.0/loop_memory/wiki/classifier.py +238 -0
- loop_memory-0.4.0/loop_memory/wiki/prompts.py +295 -0
- loop_memory-0.4.0/loop_memory/wiki/scope.py +227 -0
- loop_memory-0.4.0/loop_memory.egg-info/PKG-INFO +627 -0
- loop_memory-0.4.0/loop_memory.egg-info/SOURCES.txt +122 -0
- loop_memory-0.4.0/loop_memory.egg-info/dependency_links.txt +1 -0
- loop_memory-0.4.0/loop_memory.egg-info/entry_points.txt +2 -0
- loop_memory-0.4.0/loop_memory.egg-info/requires.txt +25 -0
- loop_memory-0.4.0/loop_memory.egg-info/top_level.txt +1 -0
- loop_memory-0.4.0/pyproject.toml +84 -0
- loop_memory-0.4.0/setup.cfg +4 -0
- loop_memory-0.4.0/tests/test_admin_ingest_route.py +102 -0
- loop_memory-0.4.0/tests/test_agent_memory_api.py +163 -0
- loop_memory-0.4.0/tests/test_agent_memory_sdk.py +184 -0
- loop_memory-0.4.0/tests/test_auth_token_rotate.py +94 -0
- loop_memory-0.4.0/tests/test_cli_v7.py +57 -0
- loop_memory-0.4.0/tests/test_contradictions.py +293 -0
- loop_memory-0.4.0/tests/test_evolution.py +184 -0
- loop_memory-0.4.0/tests/test_evolution_quality.py +360 -0
- loop_memory-0.4.0/tests/test_expanduser.py +22 -0
- loop_memory-0.4.0/tests/test_export_ask.py +251 -0
- loop_memory-0.4.0/tests/test_graph.py +172 -0
- loop_memory-0.4.0/tests/test_ingest.py +95 -0
- loop_memory-0.4.0/tests/test_llm_consolidator.py +429 -0
- loop_memory-0.4.0/tests/test_llm_fingerprint_not_persisted.py +87 -0
- loop_memory-0.4.0/tests/test_llm_providers.py +255 -0
- loop_memory-0.4.0/tests/test_llm_test_endpoint.py +106 -0
- loop_memory-0.4.0/tests/test_loop.py +56 -0
- loop_memory-0.4.0/tests/test_mcp.py +386 -0
- loop_memory-0.4.0/tests/test_memories_pagination.py +101 -0
- loop_memory-0.4.0/tests/test_openclaw_loader.py +147 -0
- loop_memory-0.4.0/tests/test_reflection.py +70 -0
- loop_memory-0.4.0/tests/test_score_api.py +67 -0
- loop_memory-0.4.0/tests/test_scoring_v2.py +119 -0
- loop_memory-0.4.0/tests/test_secrets.py +63 -0
- loop_memory-0.4.0/tests/test_serve_app.py +504 -0
- loop_memory-0.4.0/tests/test_serve_handlers.py +168 -0
- loop_memory-0.4.0/tests/test_session_order.py +75 -0
- loop_memory-0.4.0/tests/test_store.py +335 -0
- loop_memory-0.4.0/tests/test_summarization.py +106 -0
- loop_memory-0.4.0/tests/test_universal_memory.py +445 -0
- loop_memory-0.4.0/tests/test_vector_store.py +48 -0
- loop_memory-0.4.0/tests/test_watcher.py +100 -0
- loop_memory-0.4.0/tests/test_wiki_classifier.py +377 -0
- loop_memory-0.4.0/tests/test_wiki_export_escape.py +92 -0
- loop_memory-0.4.0/tests/test_wiki_prompts.py +189 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Loop Memory contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: loop-memory
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: A general-purpose, local memory system for every AI agent you run. Loop Memory auto-captures conversations from Codex / Claude / Hermes / OpenClaw, scores them by importance × recency × usage × feedback, distils them into a curated wiki, and serves everything from a single web UI.
|
|
5
|
+
Author: Loop Memory contributors <loop-memory@users.noreply.github.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/smartfind/loop-memory
|
|
8
|
+
Project-URL: Documentation, https://github.com/smartfind/loop-memory#readme
|
|
9
|
+
Project-URL: Source, https://github.com/smartfind/loop-memory
|
|
10
|
+
Project-URL: Issues, https://github.com/smartfind/loop-memory/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/smartfind/loop-memory/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: llm,memory,agent,agents,rag,long-term-memory,memory-system,codex,claude,hermes,openclaw,clawx
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Provides-Extra: openai
|
|
25
|
+
Requires-Dist: openai~=1.30; extra == "openai"
|
|
26
|
+
Provides-Extra: chroma
|
|
27
|
+
Requires-Dist: chromadb~=0.4; extra == "chroma"
|
|
28
|
+
Provides-Extra: sentence
|
|
29
|
+
Requires-Dist: sentence-transformers~=2.6; extra == "sentence"
|
|
30
|
+
Provides-Extra: serve
|
|
31
|
+
Requires-Dist: fastapi~=0.110; extra == "serve"
|
|
32
|
+
Requires-Dist: uvicorn~=0.27; extra == "serve"
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest~=8.0; extra == "dev"
|
|
35
|
+
Requires-Dist: coverage~=7.0; extra == "dev"
|
|
36
|
+
Requires-Dist: ruff~=0.5; extra == "dev"
|
|
37
|
+
Provides-Extra: all
|
|
38
|
+
Requires-Dist: openai~=1.30; extra == "all"
|
|
39
|
+
Requires-Dist: chromadb~=0.4; extra == "all"
|
|
40
|
+
Requires-Dist: sentence-transformers~=2.6; extra == "all"
|
|
41
|
+
Requires-Dist: fastapi~=0.110; extra == "all"
|
|
42
|
+
Requires-Dist: uvicorn~=0.27; extra == "all"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
<div align="center">
|
|
46
|
+
<img src="docs/assets/logo.svg" alt="Loop Memory" width="360"/>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
# Loop Memory
|
|
50
|
+
|
|
51
|
+
> **A general-purpose memory system for every AI agent you run locally.**
|
|
52
|
+
>
|
|
53
|
+
> Auto-captures every Codex / Claude / Hermes / OpenClaw conversation,
|
|
54
|
+
> distils them into a tight wiki of stable knowledge, and lets the
|
|
55
|
+
> agent recall what matters on demand.
|
|
56
|
+
|
|
57
|
+
[](https://github.com/smartfind/loop-memory/actions)
|
|
58
|
+
[](https://pypi.org/project/loop-memory/)
|
|
59
|
+
[](LICENSE)
|
|
60
|
+
[](docs/release.md)
|
|
61
|
+
[](https://pypi.org/project/loop-memory/)
|
|
62
|
+
[](pyproject.toml)
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## What it does
|
|
67
|
+
|
|
68
|
+
**Loop Memory** gives every agent you use a single, persistent brain
|
|
69
|
+
that outlives any one conversation. Each agent (Codex CLI, Claude
|
|
70
|
+
Code, Hermes, OpenClaw / clawx, …) drops its transcripts onto disk;
|
|
71
|
+
Loop Memory quietly catches them, scores every fragment by *importance
|
|
72
|
+
× recency × usage × feedback*, distils the long tail into a curated
|
|
73
|
+
wiki, and re-injects the relevant pieces into the next session.
|
|
74
|
+
|
|
75
|
+
```mermaid
|
|
76
|
+
flowchart LR
|
|
77
|
+
subgraph Capture
|
|
78
|
+
A1[Codex CLI] --> Store
|
|
79
|
+
A2[Claude Code] --> Store
|
|
80
|
+
A3[Hermes] --> Store
|
|
81
|
+
A4[OpenClaw / clawx] --> Store
|
|
82
|
+
A5[Any watcher] --> Store
|
|
83
|
+
end
|
|
84
|
+
Store[(SQLite
|
|
85
|
+
sessions + memories)]
|
|
86
|
+
Store --> Score[Signal-aware
|
|
87
|
+
scoring]
|
|
88
|
+
Score --> Cluster[Semantic
|
|
89
|
+
clustering]
|
|
90
|
+
Cluster --> Distill[Per-cluster
|
|
91
|
+
distillation]
|
|
92
|
+
Distill --> Wiki[(Curated wiki
|
|
93
|
+
preferences / decisions /
|
|
94
|
+
projects / domain)]
|
|
95
|
+
Wiki --> Recall[Next-session recall
|
|
96
|
+
via MCP / hooks]
|
|
97
|
+
Recall --> A1
|
|
98
|
+
Recall --> A2
|
|
99
|
+
Recall --> A3
|
|
100
|
+
Recall --> A4
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
*One loop, many agents, one evolving wiki.*
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Install
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install loop-memory # core: zero deps
|
|
111
|
+
pip install 'loop-memory[serve]' # + FastAPI web UI
|
|
112
|
+
pip install 'loop-memory[openai]' # + OpenAI client
|
|
113
|
+
pip install 'loop-memory[all]' # everything
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Quickstart
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# 1. Import everything that already lives on your disk
|
|
122
|
+
loop-memory ingest codex # ~/.codex/sessions/*.json
|
|
123
|
+
loop-memory ingest claude # ~/.claude/**/*.jsonl
|
|
124
|
+
loop-memory ingest hermes # ~/.hermes/**/*.jsonl
|
|
125
|
+
|
|
126
|
+
# 2. Look at it
|
|
127
|
+
loop-memory serve --port 7767 # open http://127.0.0.1:7767
|
|
128
|
+
|
|
129
|
+
# 3. Make it run on a timer
|
|
130
|
+
# (see docs/auto-capture.md for launchd / systemd / cron snippets)
|
|
131
|
+
loop-memory consolidate # rescore + GC + dedupe
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Why Loop Memory vs. every other agent-memory project
|
|
137
|
+
|
|
138
|
+
We surveyed the open-source memory systems for AI agents that came
|
|
139
|
+
up in 2026 (Mem0, Hindsight, OpenViking, A-MEM) and kept what worked.
|
|
140
|
+
Loop Memory is the smallest system that still ships *all* of the
|
|
141
|
+
following — every other project we looked at lacks at least one:
|
|
142
|
+
|
|
143
|
+
| Capability | **Loop Memory** | Mem0 v3 | Hindsight | OpenViking | A-MEM |
|
|
144
|
+
| --- | --- | --- | --- | --- | --- |
|
|
145
|
+
| Multi-source capture (Codex / Claude / Hermes / OpenClaw) | ✅ out of the box | ⚠ requires plugin per client | ⚠ hosted only | ⚠ SDK + companion app | ❌ |
|
|
146
|
+
| Local-first SQLite (zero external services) | ✅ | ❌ Postgres + Qdrant | ❌ Postgres + Qdrant | ⚠ file-system + cloud | ⚠ ChromaDB |
|
|
147
|
+
| Hybrid recall: BM25 + semantic + entity (RRF) | ✅ | ✅ | ✅ | ✅ | ⚠ entity-only |
|
|
148
|
+
| Temporal reasoning in retrieval (boost / suppress by date intent) | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
149
|
+
| Tiered loading L0/L1/L2 (titles / summary / body) | ✅ | ❌ | ❌ | ✅ | ❌ |
|
|
150
|
+
| Per-client wiki scope (global vs. source-specific) | ✅ | ⚠ user-level | ⚠ tenant-level | ❌ | ❌ |
|
|
151
|
+
| Distillation that prefers *completeness over compression* | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
152
|
+
| Distillation that runs on a schedule **and** on demand | ✅ both | ✅ schedule | ✅ both | ✅ schedule | ❌ |
|
|
153
|
+
| Knowledge graph (entities + relations) | ✅ light | ✅ Neo4j | ✅ | ✅ native graph | ✅ ChromaDB |
|
|
154
|
+
| Cognitive sleep with auditable cleanup | ✅ v7 | ⚠ | ✅ | ⚠ | ❌ |
|
|
155
|
+
| Git-friendly `MEMORY.md` export / fork | ✅ v7 | ⚠ hosted | ⚠ | ✅ file-based | ❌ |
|
|
156
|
+
| Universal SDK + HTTP + MCP contract | ✅ v7 | ✅ | ⚠ | ✅ | ⚠ |
|
|
157
|
+
| OpenAI-compatible multi-provider LLM (incl. MiniMax) | ✅ | ✅ | ✅ | ✅ | ⚠ |
|
|
158
|
+
| Open-source, MIT, no hosted tier required | ✅ | ✅ (cloud SKUs dominant) | ✅ | ⚠ AGPLv3 | ✅ |
|
|
159
|
+
|
|
160
|
+
**The honest gap**: we don't have Mem0's hosted platform (managed
|
|
161
|
+
multi-tenant scaling, byte-benchmarked vector indexes), and we don't
|
|
162
|
+
ship OpenViking's companion desktop app. What we *do* ship is the
|
|
163
|
+
smallest set of moving parts that lets you run the same memory
|
|
164
|
+
loop across every locally-installed agent without sending your
|
|
165
|
+
transcripts anywhere.
|
|
166
|
+
|
|
167
|
+
If you want raw scale, Mem0's cloud SKU will beat us. If you want a
|
|
168
|
+
local-first single-user brain that every offline agent (Codex, Claude,
|
|
169
|
+
Hermes, clawx) can read and write, we built this for you.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Architecture & docs
|
|
174
|
+
|
|
175
|
+
| Doc | What's in it |
|
|
176
|
+
| --- | --- |
|
|
177
|
+
| [docs/architecture.md](docs/architecture.md) | Layered view of the subpackages, the 5-stage evolution pipeline, the request lifecycle, and how secrets and settings are separated between the SQLite store and a local permission-restricted secrets file |
|
|
178
|
+
| [docs/api.md](docs/api.md) | HTTP API reference — every route, request body, and response shape the UI consumes |
|
|
179
|
+
| [docs/agent-memory-api.md](docs/agent-memory-api.md) | Stable four-verb SDK / HTTP / MCP contract for any Agent |
|
|
180
|
+
| [docs/universal-agent-memory.md](docs/universal-agent-memory.md) | v7 graph memory, cognitive sleep, portable bundles, namespaces, and MCP/CLI extensions |
|
|
181
|
+
| [docs/providers.md](docs/providers.md) | LLM provider reference — built-in providers, defaults, base URLs, and how to add a new one |
|
|
182
|
+
| [docs/auto-capture.md](docs/auto-capture.md) | Hooking Codex / Claude / Hermes / OpenClaw watchers (filesystem, launchd, systemd, cron) |
|
|
183
|
+
| [CONTRIBUTING.md](CONTRIBUTING.md) | Local dev loop, pytest, where secrets live, how to add a provider/source |
|
|
184
|
+
| [CHANGELOG.md](CHANGELOG.md) | Per-release notes |
|
|
185
|
+
|
|
186
|
+
The live interactive OpenAPI document is at `http://127.0.0.1:7767/docs`
|
|
187
|
+
once the server is running.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
## After install: 30-second setup
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Show me what's installed, what's wired, what's broken.
|
|
196
|
+
loop-memory doctor
|
|
197
|
+
|
|
198
|
+
# Auto-configure MCP + SessionStart hooks for every detected CLI
|
|
199
|
+
# (Codex CLI, Claude Code, Hermes).
|
|
200
|
+
loop-memory install-hooks
|
|
201
|
+
|
|
202
|
+
# Install the openclaw/clawx auto-ingest watcher (launchd on macOS).
|
|
203
|
+
loop-memory openclaw-setup
|
|
204
|
+
|
|
205
|
+
# Run it on a schedule — web UI → ⚙ Model → set "every day 03:00".
|
|
206
|
+
loop-memory serve --port 7767 # → http://127.0.0.1:7767
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The web UI also has a **🔍 Run doctor** panel under the kebab menu
|
|
210
|
+
(⌘D) that shows the same green/red diagnostic screen inline.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Auto-capture (after every conversation)
|
|
215
|
+
|
|
216
|
+
A new conversation ends → its transcript file lands in a watched
|
|
217
|
+
directory → the watcher ingests it → it shows up in the UI. Three
|
|
218
|
+
flavors:
|
|
219
|
+
|
|
220
|
+
| Tool | Watch |
|
|
221
|
+
| -------------------------- | ----------------------------------------------- |
|
|
222
|
+
| Codex CLI | `loop-memory hook --source codex --watch ~/.codex/sessions` |
|
|
223
|
+
| Claude Code | `loop-memory hook --source claude --watch ~/.claude` |
|
|
224
|
+
| Hermes | `loop-memory hook --source hermes --watch ~/.hermes` |
|
|
225
|
+
| OpenClaw (clawx) | `loop-memory hook --source openclaw --watch ~/.openclaw/agents/main/sessions` — also ingests `workspace/memory/*.md` daily logs |
|
|
226
|
+
|
|
227
|
+
Three of these in a `tmux` session, or persisted via launchd, keeps
|
|
228
|
+
your memory store fresh without any clicks. Run `loop-memory
|
|
229
|
+
consolidate` on an hourly cron to keep the scoring healthy.
|
|
230
|
+
|
|
231
|
+
See [docs/auto-capture.md](docs/auto-capture.md) for ready-to-paste
|
|
232
|
+
launchd + systemd + cron snippets.
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Dashboard + Evolution consolidator (看板 + 进化式蒸馏)
|
|
236
|
+
|
|
237
|
+
The Dashboard tab gives you a live, at-a-glance view of the memory
|
|
238
|
+
pipeline and lets you steer it.
|
|
239
|
+
|
|
240
|
+
- **4 KPI cards** — raw memory count, distilled wiki count, average
|
|
241
|
+
score, total recall events (real-time, auto-refresh every 8s).
|
|
242
|
+
- **5-stage data-flow animation** — score → cluster → distill → wiki
|
|
243
|
+
→ memo. Click any node to drill into the items that flowed through
|
|
244
|
+
it last run. The wave path on top pulses to suggest motion; nodes
|
|
245
|
+
pulse on hover.
|
|
246
|
+
- **Drill-down panel** — every item has 👍 / 👎 buttons that feed the
|
|
247
|
+
evolution loop. Negative feedback lowers the memory's importance;
|
|
248
|
+
positive bumps it. Both update the "most recalled memories" list.
|
|
249
|
+
- **Evolution run button** — invokes the 5-stage Evolution
|
|
250
|
+
Consolidator with whatever provider is currently configured.
|
|
251
|
+
|
|
252
|
+
### Evolution Consolidator (replaces the old single-pass one)
|
|
253
|
+
|
|
254
|
+
A hierarchical, signal-aware distillation pipeline designed to keep
|
|
255
|
+
your knowledge base tight and increasingly aligned with your real
|
|
256
|
+
preferences over time.
|
|
257
|
+
|
|
258
|
+
| Stage | What it does |
|
|
259
|
+
| ----- | ------------ |
|
|
260
|
+
| 1. Signal-Aware Scoring | Blends `importance × recency` with `recall_count` (+0..0.10) and `negative` feedback (-0..0.15), so items the user actually uses float to the top. |
|
|
261
|
+
| 2. Semantic Batching | Greedy cosine clustering using a hashed embedding; clusters ≤15 items each, threshold 0.35. |
|
|
262
|
+
| 3. Per-Cluster Distillation | LLM returns per-row `keep / importance / distill / tags` actions. Row-level rewrites only when the LLM is confident. |
|
|
263
|
+
| 4. Hierarchical Wiki | Cluster summaries + existing wiki + the **evolution memo** feed the LLM, which produces / updates pages bucketed into `preferences / decisions / projects / domain / feedback`. Slugs are stable, so re-running merges. |
|
|
264
|
+
| 5. Evolution Memo | Persists `{rescored, dropped, wiki_created, wiki_updated, notes}` for the last run; next run's Stage-4 prompt includes it so the LLM keeps learning the user's preferences across runs. |
|
|
265
|
+
|
|
266
|
+
Run it manually:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
loop-memory consolidate # legacy single-pass
|
|
270
|
+
curl -X POST http://127.0.0.1:7767/api/admin/evolution/run # 5-stage
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
### Scoring v2: time × usage × feedback
|
|
276
|
+
|
|
277
|
+
The score of every memory is a weighted blend of **four** components,
|
|
278
|
+
not just importance × recency:
|
|
279
|
+
|
|
280
|
+
| Component | Weight | What it measures |
|
|
281
|
+
| --------- | ------ | ---------------- |
|
|
282
|
+
| `importance` | 0.40 | Original LLM/original importance in [0, 1] |
|
|
283
|
+
| `recency` | 0.25 | Time decay: `½^(age / half_life)`, default half_life 30 days |
|
|
284
|
+
| `usage` | 0.25 | `log1p(recall_count)/log1p(100) × recency_of_last_recall` |
|
|
285
|
+
| `feedback` | 0.10 | `tanh((positive - negative) / 3)` — sticky (no time decay) |
|
|
286
|
+
|
|
287
|
+
The blend is normalised to [0, 1]. **What this means in practice**:
|
|
288
|
+
recent + useful memories float up; old + unused memories sink;
|
|
289
|
+
memories the user explicitly 👍 stay high; 👎 ones stay low even
|
|
290
|
+
if they were popular once.
|
|
291
|
+
|
|
292
|
+
API endpoints to inspect the breakdown:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
curl localhost:7767/api/memories/<id>/score # 4 components
|
|
296
|
+
curl localhost:7767/api/pipeline/score-distribution # 10-bin histogram
|
|
297
|
+
curl localhost:7767/api/pipeline/decay-stats # age buckets × avg score
|
|
298
|
+
curl -XPOST 'localhost:7767/api/admin/bump-recall?ids=<id>' # simulate LLM recall
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Dashboard v2: real animation, real charts
|
|
302
|
+
|
|
303
|
+
The Dashboard tab has been rebuilt end-to-end:
|
|
304
|
+
|
|
305
|
+
- **5 KPI cards** with live sparklines (60-sample rolling history).
|
|
306
|
+
- **Active-stage card** — shows the pipeline stage currently running,
|
|
307
|
+
switching automatically as `pipeline_runs` update.
|
|
308
|
+
- **Animated data flow** — particle dots travel left-to-right along
|
|
309
|
+
the SVG path whenever a stage is running; nodes pulse with the
|
|
310
|
+
active stage highlighted. Click any node to drill down.
|
|
311
|
+
- **Score distribution histogram** — 10 bins of v2 score, hover for
|
|
312
|
+
exact counts.
|
|
313
|
+
- **Time-decay chart** — bars are count per age bucket, the line on
|
|
314
|
+
top plots average score so you can *see* the decay curve.
|
|
315
|
+
- **Per-memory score breakdown** — every drill-down item has a
|
|
316
|
+
"why?" button that expands a 4-bar breakdown (importance /
|
|
317
|
+
recency / usage / feedback).
|
|
318
|
+
- **↻ bump button** on each item — lets you mark a memory as
|
|
319
|
+
"just consulted by the LLM" so its usage component goes up and
|
|
320
|
+
it ranks higher next time.
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
### Feedback loop
|
|
324
|
+
|
|
325
|
+
User signals close the loop:
|
|
326
|
+
|
|
327
|
+
- 👍 on a drill-down item → `positive++`, `importance += 0.05`
|
|
328
|
+
- 👎 → `negative++`, `importance -= 0.05`
|
|
329
|
+
- Every `recall()` / search bumps `recall_count` on the returned
|
|
330
|
+
rows so the next Stage-1 ranks them higher.
|
|
331
|
+
|
|
332
|
+
## Auto-feedback into every LLM client (反哺)
|
|
333
|
+
|
|
334
|
+
Distilled knowledge is only useful if your LLM tools can actually
|
|
335
|
+
read it. Loop Memory ships with three zero-dep commands that wire
|
|
336
|
+
the memory store into Codex CLI, Claude Code and Hermes
|
|
337
|
+
automatically:
|
|
338
|
+
|
|
339
|
+
| Command | What it does |
|
|
340
|
+
| -------------------------------- | ---------------------------------------------------------------------------- |
|
|
341
|
+
| `loop-memory install-hooks` | Auto-detect `~/.codex`, `~/.claude`, `~/.hermes` and write MCP + SessionStart hook configs in place. Idempotent — re-run any time. |
|
|
342
|
+
| `loop-memory inject [query]` | Print a `# Long-term memory context` markdown block (distilled wiki + recent relevant memories) for a SessionStart hook. |
|
|
343
|
+
| `loop-memory mcp` | Run the **stdio MCP server** with memory, graph, and cognitive tools (`recall`, `remember`, `forget`, `feedback`, `remember_edge`, `subgraph`, `cognitive_sleep`, `audit`, and wiki tools). |
|
|
344
|
+
|
|
345
|
+
Quick setup on a fresh machine:
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
pip install loop-memory
|
|
349
|
+
loop-memory install-hooks # writes ~/.codex/config.toml + ~/.claude/{mcp.json,settings.json} + ~/.hermes/mcp.json
|
|
350
|
+
# restart Codex / Claude Code / Hermes and the next session will:
|
|
351
|
+
# 1) auto-inject the distilled wiki as the first user message (SessionStart hook)
|
|
352
|
+
# 2) expose `recall` / `list_wiki` / `get_wiki` MCP tools so the model can pull more on demand
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Manual smoke-test without restarting the client:
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
loop-memory inject # dumps the warm-start block to stdout
|
|
359
|
+
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}\n{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"wiki_summary"}}\n' \
|
|
360
|
+
| loop-memory mcp # round-trips JSON-RPC over stdio
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
The MCP server speaks JSON-RPC 2.0 over newline-delimited stdin/stdout,
|
|
364
|
+
uses no third-party deps, and is safe to launch per-client (Claude Code,
|
|
365
|
+
Codex CLI, Hermes each spawn their own process). OpenClaw is detected but
|
|
366
|
+
currently needs `loop-memory hook --source openclaw --watch
|
|
367
|
+
~/.openclaw/sessions &` to start its watcher.
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Web UI
|
|
373
|
+
|
|
374
|
+
`loop-memory serve` opens a small local page at
|
|
375
|
+
`http://127.0.0.1:7767` with four primary views:
|
|
376
|
+
|
|
377
|
+
- **Timeline**: searchable session history and scored memories from every client.
|
|
378
|
+
- **Dashboard**: lifecycle, source health, distillation progress, weekly report,
|
|
379
|
+
contradictions, audit data, and the end-to-end memory architecture.
|
|
380
|
+
- **Wiki**: distilled, editable knowledge pages with export and Ask workflows.
|
|
381
|
+
- **Knowledge graph**: an interactive globe built from distilled Wiki knowledge.
|
|
382
|
+
|
|
383
|
+
Top-right actions provide one-click import, re-scoring, AI consolidation, model
|
|
384
|
+
configuration, scheduling, language switching, and light/dark themes.
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Time-weighted scoring
|
|
389
|
+
|
|
390
|
+
Every memory carries a `score ∈ [0, 1]` recomputed from:
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
score = 0.35 · importance + 0.65 · recency
|
|
394
|
+
recency = ½ ^ (age / half_life)
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
`half_life` defaults to 30 days, configurable via
|
|
398
|
+
`consolidate(half_life_days=...)`. The UI shows the score as a
|
|
399
|
+
percentage; use `?min_score=0.85` to see only high-relevance memories.
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Programmatic use
|
|
404
|
+
|
|
405
|
+
```python
|
|
406
|
+
from loop_memory import MemoryStore
|
|
407
|
+
from loop_memory.ingest.loader import get_loader
|
|
408
|
+
from loop_memory.ingest.pipeline import MemoryPipeline
|
|
409
|
+
from loop_memory.backends.embedding import HashingEmbedder
|
|
410
|
+
from loop_memory.jobs.consolidate import Consolidator
|
|
411
|
+
|
|
412
|
+
store = MemoryStore("~/.loop_memory/loop_memory.db")
|
|
413
|
+
pipeline = MemoryPipeline(store, embedder=HashingEmbedder(dim=128))
|
|
414
|
+
|
|
415
|
+
loader = get_loader("claude")
|
|
416
|
+
for path in loader.discover():
|
|
417
|
+
session = loader.load_one(path)
|
|
418
|
+
if session:
|
|
419
|
+
pipeline.run(session)
|
|
420
|
+
|
|
421
|
+
# background-style consolidation
|
|
422
|
+
report = Consolidator(store, embedder=HashingEmbedder(dim=128)).run()
|
|
423
|
+
print(report)
|
|
424
|
+
# ConsolidateReport(rescored=15, gc_removed=0, merged=0, elapsed_ms=2.88)
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
Or just keep using the engine inside a Python process:
|
|
428
|
+
|
|
429
|
+
```python
|
|
430
|
+
from loop_memory import LoopEngine, EchoLLM, HashingEmbedder
|
|
431
|
+
engine = LoopEngine(llm=EchoLLM(), embedder=HashingEmbedder(dim=128))
|
|
432
|
+
print(engine.turn("Hi! I'm Mia and I love matcha.").reply)
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## The four-stage loop
|
|
438
|
+
|
|
439
|
+
Even though v0.2 is built around local storage, the original
|
|
440
|
+
`Retrieve → Generate → Reflect → Store` loop engine is still here:
|
|
441
|
+
|
|
442
|
+
| Stage | Default impl | Replace with |
|
|
443
|
+
| ---------- | ------------------------------ | ---------------------------------- |
|
|
444
|
+
| RETRIEVE | cosine + importance × recency | any `VectorStore` (Chroma, FAISS…) |
|
|
445
|
+
| GENERATE | any `LLMClient` | OpenAI, Anthropic, local, … |
|
|
446
|
+
| REFLECT | regex fact extractor | an LLM-based reflector |
|
|
447
|
+
| STORE | short-term + episodic + LTM | persistent store via extras |
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Project layout
|
|
452
|
+
|
|
453
|
+
```
|
|
454
|
+
loop_memory/
|
|
455
|
+
loop_memory/
|
|
456
|
+
memory/types.py # MemoryItem + 4 tiers
|
|
457
|
+
backends/embedding.py # BaseEmbedder, HashingEmbedder, IdentityEmbedder
|
|
458
|
+
backends/vector_store.py # VectorStore protocol + InMemory / Chroma
|
|
459
|
+
backends/sentence_embedder.py # optional sentence-transformers
|
|
460
|
+
llm/base.py # LLMClient protocol + EchoLLM + helpers
|
|
461
|
+
llm/openai_adapter.py # optional OpenAI client
|
|
462
|
+
engine/loop.py # the Retrieve → Generate → Reflect → Store loop
|
|
463
|
+
engine/reflect.py # reflection & summarization passes
|
|
464
|
+
storage/sqlite_store.py # persistent SQLite-backed MemoryStore
|
|
465
|
+
ingest/loader.py # CodexLoader, ClaudeLoader, HermesLoader
|
|
466
|
+
ingest/pipeline.py # session → MemoryStore
|
|
467
|
+
jobs/consolidate.py # background rescore + GC + dedupe
|
|
468
|
+
serve/app.py # FastAPI app for the local web UI
|
|
469
|
+
serve/static/index.html # the page
|
|
470
|
+
serve/watcher.py # filesystem watcher for auto-capture
|
|
471
|
+
cli/main.py # CLI entrypoint (chat / stats / ingest / consolidate / serve / hook)
|
|
472
|
+
examples/demo.py # runnable, zero-API-key demo
|
|
473
|
+
py.typed
|
|
474
|
+
tests/ # 92 unit tests, zero deps
|
|
475
|
+
docs/auto-capture.md # launchd / systemd / cron recipes
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## Security & auth token
|
|
481
|
+
|
|
482
|
+
Loop Memory ships with a CSP deny-all + Origin-bound CSRF policy on every
|
|
483
|
+
state-changing request, parameterised SQL throughout, and a Keychain-backed
|
|
484
|
+
secret store (`~/.loop_memory/secrets.json` mode 0600 on Linux). The web UI
|
|
485
|
+
also auto-sanitises any markdown it renders (DOMParser + tag allowlist + URL
|
|
486
|
+
scheme scrubber — see `loop_memory/serve/static/js/lib/sanitize.js`).
|
|
487
|
+
|
|
488
|
+
**Auth token (recommended on first run):**
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
# Generates a 256-bit URL-safe token, stored hashed in the settings table.
|
|
492
|
+
# The server stays authenticated forever — there is no "disable" path.
|
|
493
|
+
curl -X POST http://127.0.0.1:7767/api/admin/auth/token | tee token.txt
|
|
494
|
+
|
|
495
|
+
# Rotate later (use this when you suspect the token has leaked):
|
|
496
|
+
curl -X DELETE http://127.0.0.1:7767/api/admin/auth/token \
|
|
497
|
+
-H "Authorization: Bearer $(cat token.txt)" | tee token.txt
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
Pass the token as `Authorization: Bearer …` header on every admin call.
|
|
501
|
+
The web UI stores it in `localStorage` under `loop_auth_token` and attaches
|
|
502
|
+
it automatically; non-browser clients (curl, MCP, SDK) must opt in by
|
|
503
|
+
passing it explicitly.
|
|
504
|
+
|
|
505
|
+
**When to set a token:**
|
|
506
|
+
|
|
507
|
+
- ✅ Always, even on loopback. The default of "no token" exists only as a
|
|
508
|
+
TOFU bootstrap path; an unconfigured server trusts *any* browser on
|
|
509
|
+
localhost to mutate state (CSRF still rejects cross-origin POSTs from
|
|
510
|
+
a remote page, but a malicious local app can still call the API).
|
|
511
|
+
- ✅ Especially if you ever bind to anything other than `127.0.0.1`
|
|
512
|
+
(`loop-memory serve --host 0.0.0.0` now prints a security warning).
|
|
513
|
+
- ❌ Never `DELETE /api/admin/auth/token` to "disable" auth — it rotates
|
|
514
|
+
to a fresh token instead (the audit found that fully disabling auth
|
|
515
|
+
was the easiest way back to the no-token state).
|
|
516
|
+
|
|
517
|
+
**Threat model notes:**
|
|
518
|
+
|
|
519
|
+
- `install-hooks` runs Python that touches `~/.*` — it's gated by the
|
|
520
|
+
bearer token like every other `POST /api/admin/*` route.
|
|
521
|
+
- The `<private>...</private>` span stripping in the privacy layer keeps
|
|
522
|
+
user-marked secrets out of long-term storage; the regex redaction layer
|
|
523
|
+
then catches API keys / tokens / private keys / JWTs / generic
|
|
524
|
+
high-entropy blobs before they reach SQLite.
|
|
525
|
+
- The bundled weekly-report Markdown is rendered through the
|
|
526
|
+
`sanitizeHtml` sanitizer (see `tests-js/test_sanitize.test.mjs` for
|
|
527
|
+
the bypass coverage).
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## Wiki scope auto-classification
|
|
532
|
+
|
|
533
|
+
New wiki pages use a local, deterministic scope evaluator by default:
|
|
534
|
+
|
|
535
|
+
- Universal security guidance (for example, rotating API keys, never pasting
|
|
536
|
+
secrets, or using parameterised SQL) is automatically promoted to
|
|
537
|
+
`scope="global"` when the classifier has enough security and cross-client
|
|
538
|
+
signals.
|
|
539
|
+
- Preferences, personal facts, project incidents, and other knowledge default
|
|
540
|
+
to the client that supplied the evidence (`codex`, `claude`, `hermes`, or
|
|
541
|
+
`openclaw`). Pages with no source metadata use the privacy-preserving
|
|
542
|
+
`codex` fallback rather than being shared with every client.
|
|
543
|
+
- An explicit `scope` always wins. Use `scope="auto"` (or omit it) to ask the
|
|
544
|
+
evaluator for a recommendation. Existing pages are not migrated, and an
|
|
545
|
+
update that omits `scope` preserves its current manual scope.
|
|
546
|
+
- Every decision is stored in the page's `auto_classification` audit object;
|
|
547
|
+
inspect it with `GET /api/wiki/{page_id}/classification-history` or preview
|
|
548
|
+
a decision with `POST /api/wiki/classify`.
|
|
549
|
+
|
|
550
|
+
The behavior is controlled by `GET/PUT /api/admin/wiki/scope`:
|
|
551
|
+
`{"enabled": true, "mode": "pattern"}` is the default. `mode="off"`
|
|
552
|
+
keeps new pages client-scoped without automatic global promotion. The
|
|
553
|
+
classifier is local and makes no model or network request on a wiki write.
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## Run the tests
|
|
558
|
+
|
|
559
|
+
```bash
|
|
560
|
+
# Python suite (memory + SDK + serve + CLI)
|
|
561
|
+
python -m pytest -q
|
|
562
|
+
python -m unittest discover -s tests -v
|
|
563
|
+
|
|
564
|
+
# Frontend sanitizer bypass suite (jsdom)
|
|
565
|
+
npm test
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
## Using distilled knowledge in your clients
|
|
571
|
+
|
|
572
|
+
After running `loop-memory consolidate` (or letting the scheduler do it), your
|
|
573
|
+
memories get distilled into durable **wiki pages**. Three ways to use them in
|
|
574
|
+
Claude / Codex / Hermes / OpenClaw:
|
|
575
|
+
|
|
576
|
+
### 1. Quick paste — `loop-memory ask`
|
|
577
|
+
|
|
578
|
+
Works from any terminal, **no server required**:
|
|
579
|
+
|
|
580
|
+
```bash
|
|
581
|
+
loop-memory ask "what does the user prefer for X?"
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
Prints a paste-ready context block to stdout. Put it as the first message of a
|
|
585
|
+
new session in any LLM client.
|
|
586
|
+
|
|
587
|
+
### 2. Whole wiki export
|
|
588
|
+
|
|
589
|
+
```bash
|
|
590
|
+
# Legacy single-file markdown export (kept for existing scripts)
|
|
591
|
+
loop-memory export
|
|
592
|
+
loop-memory export --out ~/Notes/user.md --q "preferences"
|
|
593
|
+
|
|
594
|
+
# v7 portable bundle: MEMORY.md + pages + memories + graph + metadata
|
|
595
|
+
loop-memory export ~/Notes/loop-memory-bundle
|
|
596
|
+
loop-memory export-bundle ~/Notes/loop-memory-bundle
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
Or in the UI: open the **Wiki** tab → click **⇩ Export**. A markdown file
|
|
600
|
+
downloads; paste it into your daily journal or as a system prompt.
|
|
601
|
+
|
|
602
|
+
### 3. Per-page "Copy as context" in the UI
|
|
603
|
+
|
|
604
|
+
Each wiki card has a `⎘` button that copies a single distilled page formatted
|
|
605
|
+
as background context — ready to paste as the system prompt of a fresh
|
|
606
|
+
Codex / Claude / Hermes session.
|
|
607
|
+
|
|
608
|
+
### 4. Auto-context (MCP-aware clients)
|
|
609
|
+
|
|
610
|
+
If you ran `loop-memory install-hooks`, Codex / Claude Code / Hermes will
|
|
611
|
+
automatically pull relevant memories via the MCP server. OpenClaw does not
|
|
612
|
+
support MCP — use `loop-memory ask` instead.
|
|
613
|
+
|
|
614
|
+
### Manual trigger
|
|
615
|
+
|
|
616
|
+
Click the **⚡ Run now** button (top-right) or run:
|
|
617
|
+
|
|
618
|
+
```bash
|
|
619
|
+
loop-memory consolidate-now # ask the running server to start a pass right now
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
This uses your configured model, batch size, and provider — same as the
|
|
623
|
+
scheduled runs.
|
|
624
|
+
|
|
625
|
+
## License
|
|
626
|
+
|
|
627
|
+
[MIT](LICENSE)
|