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.
Files changed (124) hide show
  1. loop_memory-0.4.0/LICENSE +21 -0
  2. loop_memory-0.4.0/PKG-INFO +627 -0
  3. loop_memory-0.4.0/README.md +583 -0
  4. loop_memory-0.4.0/loop_memory/__init__.py +62 -0
  5. loop_memory-0.4.0/loop_memory/backends/__init__.py +13 -0
  6. loop_memory-0.4.0/loop_memory/backends/embedding.py +82 -0
  7. loop_memory-0.4.0/loop_memory/backends/sentence_embedder.py +30 -0
  8. loop_memory-0.4.0/loop_memory/backends/vector_store.py +139 -0
  9. loop_memory-0.4.0/loop_memory/cli/__init__.py +0 -0
  10. loop_memory-0.4.0/loop_memory/cli/_common.py +68 -0
  11. loop_memory-0.4.0/loop_memory/cli/commands/__init__.py +13 -0
  12. loop_memory-0.4.0/loop_memory/cli/commands/cognitive.py +205 -0
  13. loop_memory-0.4.0/loop_memory/cli/commands/diag.py +346 -0
  14. loop_memory-0.4.0/loop_memory/cli/commands/graph.py +21 -0
  15. loop_memory-0.4.0/loop_memory/cli/commands/hooks.py +212 -0
  16. loop_memory-0.4.0/loop_memory/cli/commands/read.py +362 -0
  17. loop_memory-0.4.0/loop_memory/cli/commands/serve.py +147 -0
  18. loop_memory-0.4.0/loop_memory/cli/commands/write.py +138 -0
  19. loop_memory-0.4.0/loop_memory/cli/main.py +115 -0
  20. loop_memory-0.4.0/loop_memory/engine/__init__.py +0 -0
  21. loop_memory-0.4.0/loop_memory/engine/loop.py +247 -0
  22. loop_memory-0.4.0/loop_memory/engine/reflect.py +89 -0
  23. loop_memory-0.4.0/loop_memory/examples/__init__.py +0 -0
  24. loop_memory-0.4.0/loop_memory/examples/demo.py +39 -0
  25. loop_memory-0.4.0/loop_memory/export/__init__.py +39 -0
  26. loop_memory-0.4.0/loop_memory/export/memory_md.py +629 -0
  27. loop_memory-0.4.0/loop_memory/graph/__init__.py +0 -0
  28. loop_memory-0.4.0/loop_memory/graph/build.py +259 -0
  29. loop_memory-0.4.0/loop_memory/graph/extract.py +197 -0
  30. loop_memory-0.4.0/loop_memory/ingest/__init__.py +0 -0
  31. loop_memory-0.4.0/loop_memory/ingest/loader.py +782 -0
  32. loop_memory-0.4.0/loop_memory/ingest/pipeline.py +458 -0
  33. loop_memory-0.4.0/loop_memory/jobs/__init__.py +0 -0
  34. loop_memory-0.4.0/loop_memory/jobs/cognitive.py +353 -0
  35. loop_memory-0.4.0/loop_memory/jobs/compact.py +371 -0
  36. loop_memory-0.4.0/loop_memory/jobs/consolidate.py +95 -0
  37. loop_memory-0.4.0/loop_memory/jobs/contradiction.py +281 -0
  38. loop_memory-0.4.0/loop_memory/jobs/evolution.py +2021 -0
  39. loop_memory-0.4.0/loop_memory/jobs/graph.py +395 -0
  40. loop_memory-0.4.0/loop_memory/jobs/llm_compact_pass.py +24 -0
  41. loop_memory-0.4.0/loop_memory/jobs/llm_consolidate.py +980 -0
  42. loop_memory-0.4.0/loop_memory/jobs/scheduler.py +495 -0
  43. loop_memory-0.4.0/loop_memory/llm/__init__.py +0 -0
  44. loop_memory-0.4.0/loop_memory/llm/base.py +80 -0
  45. loop_memory-0.4.0/loop_memory/llm/openai_adapter.py +31 -0
  46. loop_memory-0.4.0/loop_memory/llm/providers.py +517 -0
  47. loop_memory-0.4.0/loop_memory/mcp/__init__.py +804 -0
  48. loop_memory-0.4.0/loop_memory/memory/__init__.py +0 -0
  49. loop_memory-0.4.0/loop_memory/memory/types.py +199 -0
  50. loop_memory-0.4.0/loop_memory/privacy/__init__.py +22 -0
  51. loop_memory-0.4.0/loop_memory/privacy/private.py +46 -0
  52. loop_memory-0.4.0/loop_memory/privacy/redact.py +188 -0
  53. loop_memory-0.4.0/loop_memory/py.typed +0 -0
  54. loop_memory-0.4.0/loop_memory/sdk.py +875 -0
  55. loop_memory-0.4.0/loop_memory/sdk_extensions.py +384 -0
  56. loop_memory-0.4.0/loop_memory/security/__init__.py +20 -0
  57. loop_memory-0.4.0/loop_memory/security/secrets.py +464 -0
  58. loop_memory-0.4.0/loop_memory/serve/__init__.py +0 -0
  59. loop_memory-0.4.0/loop_memory/serve/app.py +506 -0
  60. loop_memory-0.4.0/loop_memory/serve/handlers.py +316 -0
  61. loop_memory-0.4.0/loop_memory/serve/routes/_shared.py +59 -0
  62. loop_memory-0.4.0/loop_memory/serve/routes/admin.py +970 -0
  63. loop_memory-0.4.0/loop_memory/serve/routes/cognitive.py +64 -0
  64. loop_memory-0.4.0/loop_memory/serve/routes/export.py +65 -0
  65. loop_memory-0.4.0/loop_memory/serve/routes/graph.py +101 -0
  66. loop_memory-0.4.0/loop_memory/serve/routes/insights.py +702 -0
  67. loop_memory-0.4.0/loop_memory/serve/routes/memories.py +435 -0
  68. loop_memory-0.4.0/loop_memory/serve/routes/sessions.py +75 -0
  69. loop_memory-0.4.0/loop_memory/serve/routes/system.py +493 -0
  70. loop_memory-0.4.0/loop_memory/serve/routes/wiki.py +812 -0
  71. loop_memory-0.4.0/loop_memory/serve/static/__init__.py +0 -0
  72. loop_memory-0.4.0/loop_memory/serve/static/index.html +15 -0
  73. loop_memory-0.4.0/loop_memory/serve/watcher.py +451 -0
  74. loop_memory-0.4.0/loop_memory/storage/__init__.py +5 -0
  75. loop_memory-0.4.0/loop_memory/storage/retrieval.py +365 -0
  76. loop_memory-0.4.0/loop_memory/storage/sqlite_store.py +3627 -0
  77. loop_memory-0.4.0/loop_memory/wiki/__init__.py +41 -0
  78. loop_memory-0.4.0/loop_memory/wiki/backfill.py +143 -0
  79. loop_memory-0.4.0/loop_memory/wiki/classifier.py +238 -0
  80. loop_memory-0.4.0/loop_memory/wiki/prompts.py +295 -0
  81. loop_memory-0.4.0/loop_memory/wiki/scope.py +227 -0
  82. loop_memory-0.4.0/loop_memory.egg-info/PKG-INFO +627 -0
  83. loop_memory-0.4.0/loop_memory.egg-info/SOURCES.txt +122 -0
  84. loop_memory-0.4.0/loop_memory.egg-info/dependency_links.txt +1 -0
  85. loop_memory-0.4.0/loop_memory.egg-info/entry_points.txt +2 -0
  86. loop_memory-0.4.0/loop_memory.egg-info/requires.txt +25 -0
  87. loop_memory-0.4.0/loop_memory.egg-info/top_level.txt +1 -0
  88. loop_memory-0.4.0/pyproject.toml +84 -0
  89. loop_memory-0.4.0/setup.cfg +4 -0
  90. loop_memory-0.4.0/tests/test_admin_ingest_route.py +102 -0
  91. loop_memory-0.4.0/tests/test_agent_memory_api.py +163 -0
  92. loop_memory-0.4.0/tests/test_agent_memory_sdk.py +184 -0
  93. loop_memory-0.4.0/tests/test_auth_token_rotate.py +94 -0
  94. loop_memory-0.4.0/tests/test_cli_v7.py +57 -0
  95. loop_memory-0.4.0/tests/test_contradictions.py +293 -0
  96. loop_memory-0.4.0/tests/test_evolution.py +184 -0
  97. loop_memory-0.4.0/tests/test_evolution_quality.py +360 -0
  98. loop_memory-0.4.0/tests/test_expanduser.py +22 -0
  99. loop_memory-0.4.0/tests/test_export_ask.py +251 -0
  100. loop_memory-0.4.0/tests/test_graph.py +172 -0
  101. loop_memory-0.4.0/tests/test_ingest.py +95 -0
  102. loop_memory-0.4.0/tests/test_llm_consolidator.py +429 -0
  103. loop_memory-0.4.0/tests/test_llm_fingerprint_not_persisted.py +87 -0
  104. loop_memory-0.4.0/tests/test_llm_providers.py +255 -0
  105. loop_memory-0.4.0/tests/test_llm_test_endpoint.py +106 -0
  106. loop_memory-0.4.0/tests/test_loop.py +56 -0
  107. loop_memory-0.4.0/tests/test_mcp.py +386 -0
  108. loop_memory-0.4.0/tests/test_memories_pagination.py +101 -0
  109. loop_memory-0.4.0/tests/test_openclaw_loader.py +147 -0
  110. loop_memory-0.4.0/tests/test_reflection.py +70 -0
  111. loop_memory-0.4.0/tests/test_score_api.py +67 -0
  112. loop_memory-0.4.0/tests/test_scoring_v2.py +119 -0
  113. loop_memory-0.4.0/tests/test_secrets.py +63 -0
  114. loop_memory-0.4.0/tests/test_serve_app.py +504 -0
  115. loop_memory-0.4.0/tests/test_serve_handlers.py +168 -0
  116. loop_memory-0.4.0/tests/test_session_order.py +75 -0
  117. loop_memory-0.4.0/tests/test_store.py +335 -0
  118. loop_memory-0.4.0/tests/test_summarization.py +106 -0
  119. loop_memory-0.4.0/tests/test_universal_memory.py +445 -0
  120. loop_memory-0.4.0/tests/test_vector_store.py +48 -0
  121. loop_memory-0.4.0/tests/test_watcher.py +100 -0
  122. loop_memory-0.4.0/tests/test_wiki_classifier.py +377 -0
  123. loop_memory-0.4.0/tests/test_wiki_export_escape.py +92 -0
  124. 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
+ [![CI](https://img.shields.io/github/actions/workflow/status/smartfind/loop-memory/tests.yml?branch=main&style=flat-square)](https://github.com/smartfind/loop-memory/actions)
58
+ [![PyPI](https://img.shields.io/pypi/v/loop-memory.svg?style=flat-square)](https://pypi.org/project/loop-memory/)
59
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE)
60
+ [![Release guide](https://img.shields.io/badge/release-guide-blueviolet.svg?style=flat-square)](docs/release.md)
61
+ [![Python](https://img.shields.io/pypi/pyversions/loop-memory?style=flat-square)](https://pypi.org/project/loop-memory/)
62
+ [![Zero deps](https://img.shields.io/badge/dependencies-0-success.svg?style=flat-square)](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)