loop-memory 0.4.0__tar.gz → 0.4.2__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 (127) hide show
  1. {loop_memory-0.4.0 → loop_memory-0.4.2}/PKG-INFO +182 -43
  2. {loop_memory-0.4.0 → loop_memory-0.4.2}/README.md +181 -42
  3. loop_memory-0.4.2/loop_memory/cli/main.py +184 -0
  4. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/static/index.html +1 -1
  5. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory.egg-info/PKG-INFO +182 -43
  6. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory.egg-info/SOURCES.txt +2 -0
  7. {loop_memory-0.4.0 → loop_memory-0.4.2}/pyproject.toml +1 -1
  8. loop_memory-0.4.2/tests/test_cli_version.py +130 -0
  9. loop_memory-0.4.2/tests/test_repo_framing.py +287 -0
  10. loop_memory-0.4.0/loop_memory/cli/main.py +0 -115
  11. {loop_memory-0.4.0 → loop_memory-0.4.2}/LICENSE +0 -0
  12. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/__init__.py +0 -0
  13. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/backends/__init__.py +0 -0
  14. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/backends/embedding.py +0 -0
  15. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/backends/sentence_embedder.py +0 -0
  16. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/backends/vector_store.py +0 -0
  17. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/cli/__init__.py +0 -0
  18. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/cli/_common.py +0 -0
  19. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/cli/commands/__init__.py +0 -0
  20. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/cli/commands/cognitive.py +0 -0
  21. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/cli/commands/diag.py +0 -0
  22. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/cli/commands/graph.py +0 -0
  23. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/cli/commands/hooks.py +0 -0
  24. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/cli/commands/read.py +0 -0
  25. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/cli/commands/serve.py +0 -0
  26. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/cli/commands/write.py +0 -0
  27. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/engine/__init__.py +0 -0
  28. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/engine/loop.py +0 -0
  29. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/engine/reflect.py +0 -0
  30. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/examples/__init__.py +0 -0
  31. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/examples/demo.py +0 -0
  32. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/export/__init__.py +0 -0
  33. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/export/memory_md.py +0 -0
  34. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/graph/__init__.py +0 -0
  35. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/graph/build.py +0 -0
  36. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/graph/extract.py +0 -0
  37. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/ingest/__init__.py +0 -0
  38. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/ingest/loader.py +0 -0
  39. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/ingest/pipeline.py +0 -0
  40. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/jobs/__init__.py +0 -0
  41. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/jobs/cognitive.py +0 -0
  42. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/jobs/compact.py +0 -0
  43. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/jobs/consolidate.py +0 -0
  44. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/jobs/contradiction.py +0 -0
  45. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/jobs/evolution.py +0 -0
  46. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/jobs/graph.py +0 -0
  47. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/jobs/llm_compact_pass.py +0 -0
  48. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/jobs/llm_consolidate.py +0 -0
  49. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/jobs/scheduler.py +0 -0
  50. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/llm/__init__.py +0 -0
  51. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/llm/base.py +0 -0
  52. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/llm/openai_adapter.py +0 -0
  53. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/llm/providers.py +0 -0
  54. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/mcp/__init__.py +0 -0
  55. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/memory/__init__.py +0 -0
  56. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/memory/types.py +0 -0
  57. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/privacy/__init__.py +0 -0
  58. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/privacy/private.py +0 -0
  59. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/privacy/redact.py +0 -0
  60. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/py.typed +0 -0
  61. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/sdk.py +0 -0
  62. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/sdk_extensions.py +0 -0
  63. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/security/__init__.py +0 -0
  64. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/security/secrets.py +0 -0
  65. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/__init__.py +0 -0
  66. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/app.py +0 -0
  67. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/handlers.py +0 -0
  68. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/routes/_shared.py +0 -0
  69. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/routes/admin.py +0 -0
  70. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/routes/cognitive.py +0 -0
  71. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/routes/export.py +0 -0
  72. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/routes/graph.py +0 -0
  73. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/routes/insights.py +0 -0
  74. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/routes/memories.py +0 -0
  75. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/routes/sessions.py +0 -0
  76. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/routes/system.py +0 -0
  77. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/routes/wiki.py +0 -0
  78. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/static/__init__.py +0 -0
  79. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/serve/watcher.py +0 -0
  80. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/storage/__init__.py +0 -0
  81. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/storage/retrieval.py +0 -0
  82. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/storage/sqlite_store.py +0 -0
  83. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/wiki/__init__.py +0 -0
  84. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/wiki/backfill.py +0 -0
  85. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/wiki/classifier.py +0 -0
  86. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/wiki/prompts.py +0 -0
  87. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory/wiki/scope.py +0 -0
  88. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory.egg-info/dependency_links.txt +0 -0
  89. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory.egg-info/entry_points.txt +0 -0
  90. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory.egg-info/requires.txt +0 -0
  91. {loop_memory-0.4.0 → loop_memory-0.4.2}/loop_memory.egg-info/top_level.txt +0 -0
  92. {loop_memory-0.4.0 → loop_memory-0.4.2}/setup.cfg +0 -0
  93. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_admin_ingest_route.py +0 -0
  94. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_agent_memory_api.py +0 -0
  95. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_agent_memory_sdk.py +0 -0
  96. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_auth_token_rotate.py +0 -0
  97. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_cli_v7.py +0 -0
  98. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_contradictions.py +0 -0
  99. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_evolution.py +0 -0
  100. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_evolution_quality.py +0 -0
  101. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_expanduser.py +0 -0
  102. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_export_ask.py +0 -0
  103. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_graph.py +0 -0
  104. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_ingest.py +0 -0
  105. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_llm_consolidator.py +0 -0
  106. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_llm_fingerprint_not_persisted.py +0 -0
  107. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_llm_providers.py +0 -0
  108. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_llm_test_endpoint.py +0 -0
  109. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_loop.py +0 -0
  110. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_mcp.py +0 -0
  111. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_memories_pagination.py +0 -0
  112. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_openclaw_loader.py +0 -0
  113. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_reflection.py +0 -0
  114. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_score_api.py +0 -0
  115. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_scoring_v2.py +0 -0
  116. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_secrets.py +0 -0
  117. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_serve_app.py +0 -0
  118. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_serve_handlers.py +0 -0
  119. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_session_order.py +0 -0
  120. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_store.py +0 -0
  121. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_summarization.py +0 -0
  122. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_universal_memory.py +0 -0
  123. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_vector_store.py +0 -0
  124. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_watcher.py +0 -0
  125. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_wiki_classifier.py +0 -0
  126. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_wiki_export_escape.py +0 -0
  127. {loop_memory-0.4.0 → loop_memory-0.4.2}/tests/test_wiki_prompts.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: loop-memory
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
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
5
  Author: Loop Memory contributors <loop-memory@users.noreply.github.com>
6
6
  License-Expression: MIT
@@ -48,11 +48,14 @@ Dynamic: license-file
48
48
 
49
49
  # Loop Memory
50
50
 
51
- > **A general-purpose memory system for every AI agent you run locally.**
51
+ > **A general-purpose, local-first memory system that closes the agent loop for every AI agent you run.**
52
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.
53
+ > Point Loop Memory at any agent's transcript directory and it
54
+ > quietly catches every conversation, scores it, distils the long
55
+ > tail into a tight wiki of stable knowledge, and re-injects the
56
+ > relevant pieces into the next session. Out of the box: hooks for
57
+ > Codex, Claude, Hermes, and OpenClaw; an SDK + generic watcher
58
+ > CLI cover everything else.
56
59
 
57
60
  [![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
61
  [![PyPI](https://img.shields.io/pypi/v/loop-memory.svg?style=flat-square)](https://pypi.org/project/loop-memory/)
@@ -63,14 +66,51 @@ Dynamic: license-file
63
66
 
64
67
  ---
65
68
 
69
+ > **What's new in 0.4.1** — CLI dispatch hardened so every subcommand's
70
+ > `--help` / `--version` exits 0 cleanly (no more `ValueError: unknown
71
+ > source: '--help'` on a zero-deps install). Added a static
72
+ > `COMMAND_HELP` table guarded by 10 new regression cases in
73
+ > `tests/test_cli_version.py`, plus GitHub `About` / README lead now
74
+ > surface for "agent loop" searches.
75
+ > [Full changelog →](CHANGELOG.md)
76
+
77
+ ---
78
+
79
+ ## Table of contents
80
+
81
+ - [What it does](#what-it-does)
82
+ - [Install](#install)
83
+ - [Quickstart](#quickstart)
84
+ - [Why Loop Memory vs. every other agent-memory project](#why-loop-memory-vs-every-other-agent-memory-project)
85
+ - [Architecture & docs](#architecture--docs)
86
+ - [After install: 30-second setup](#after-install-30-second-setup)
87
+ - [Auto-capture (after every conversation)](#auto-capture-after-every-conversation)
88
+ - [Dashboard + Evolution consolidator](#dashboard--evolution-consolidator-看板--进化式蒸馏)
89
+ - [Auto-feedback into every LLM client](#auto-feedback-into-every-llm-client-反哺)
90
+ - [Web UI](#web-ui)
91
+ - [Programmatic use](#programmatic-use)
92
+ - [The four-stage loop](#the-four-stage-loop)
93
+ - [Project layout](#project-layout)
94
+ - [Security & auth token](#security--auth-token)
95
+ - [Wiki scope auto-classification](#wiki-scope-auto-classification)
96
+ - [FAQ & troubleshooting](#faq--troubleshooting)
97
+ - [Run the tests](#run-the-tests)
98
+ - [Using distilled knowledge in your clients](#using-distilled-knowledge-in-your-clients)
99
+ - [License](#license)
100
+
101
+ ---
102
+
66
103
  ## What it does
67
104
 
68
105
  **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.
106
+ that outlives any one conversation. Any agent that drops transcripts
107
+ on disk — Codex CLI, Claude Code, Hermes, OpenClaw / clawx, Aider,
108
+ Cursor, works; the shipped hooks cover the popular ones and a
109
+ generic watcher CLI (`loop-memory hook --source <name> --watch <dir>`)
110
+ accepts anything else. Loop Memory quietly catches every fragment,
111
+ scores it by *importance × recency × usage × feedback*, distils the
112
+ long tail into a curated wiki, and re-injects the relevant pieces
113
+ into the next session.
74
114
 
75
115
  ```mermaid
76
116
  flowchart LR
@@ -102,6 +142,21 @@ via MCP / hooks]
102
142
 
103
143
  *One loop, many agents, one evolving wiki.*
104
144
 
145
+ **Supported agents:**
146
+
147
+ | Agent | Capture path | Hook shipped? | Notes |
148
+ | -------------- | ----------------------------------- | :-----------: | ----- |
149
+ | Codex CLI | `~/.codex/sessions/*.json` | ✅ | MCP + SessionStart auto-wired by `install-hooks` |
150
+ | Claude Code | `~/.claude/**/*.jsonl` | ✅ | MCP + SessionStart auto-wired by `install-hooks` |
151
+ | Hermes | `~/.hermes/**/*.jsonl` | ✅ | MCP + SessionStart auto-wired by `install-hooks` |
152
+ | OpenClaw/clawx | `~/.openclaw/agents/main/sessions` + `workspace/memory/*.md` | ✅ | watcher only (no MCP yet); `openclaw-setup` enables launchd |
153
+ | Anything else | any on-disk transcript dir | — | use `loop-memory hook --source <name> --watch <dir>` (see [docs/auto-capture.md](docs/auto-capture.md)) |
154
+
155
+ The shipped hooks are the four popular agents we maintain in-tree.
156
+ The generic watcher CLI is the supported extension point for every
157
+ other agent — Aider, Cursor, Copilot, Cline, Continue, Goose, your
158
+ own home-grown CLI, anything that drops JSON/JSONL on disk.
159
+
105
160
  ---
106
161
 
107
162
  ## Install
@@ -142,7 +197,7 @@ following — every other project we looked at lacks at least one:
142
197
 
143
198
  | Capability | **Loop Memory** | Mem0 v3 | Hindsight | OpenViking | A-MEM |
144
199
  | --- | --- | --- | --- | --- | --- |
145
- | Multi-source capture (Codex / Claude / Hermes / OpenClaw) | ✅ out of the box | ⚠ requires plugin per client | ⚠ hosted only | ⚠ SDK + companion app | ❌ |
200
+ | Multi-source capture (any agent with on-disk transcripts) | ✅ generic watcher CLI; hooks shipped for Codex / Claude / Hermes / OpenClaw | ⚠ requires plugin per client | ⚠ hosted only | ⚠ SDK + companion app | ❌ |
146
201
  | Local-first SQLite (zero external services) | ✅ | ❌ Postgres + Qdrant | ❌ Postgres + Qdrant | ⚠ file-system + cloud | ⚠ ChromaDB |
147
202
  | Hybrid recall: BM25 + semantic + entity (RRF) | ✅ | ✅ | ✅ | ✅ | ⚠ entity-only |
148
203
  | Temporal reasoning in retrieval (boost / suppress by date intent) | ✅ | ✅ | ❌ | ❌ | ❌ |
@@ -223,6 +278,7 @@ flavors:
223
278
  | Claude Code | `loop-memory hook --source claude --watch ~/.claude` |
224
279
  | Hermes | `loop-memory hook --source hermes --watch ~/.hermes` |
225
280
  | OpenClaw (clawx) | `loop-memory hook --source openclaw --watch ~/.openclaw/agents/main/sessions` — also ingests `workspace/memory/*.md` daily logs |
281
+ | Anything else (Aider, Cursor, Copilot, Cline, Continue, Goose, …) | `loop-memory hook --source <name> --watch <path/to/transcripts>` — see [docs/auto-capture.md](docs/auto-capture.md) for loader requirements |
226
282
 
227
283
  Three of these in a `tmux` session, or persisted via launchd, keeps
228
284
  your memory store fresh without any clicks. Run `loop-memory
@@ -329,6 +385,34 @@ User signals close the loop:
329
385
  - Every `recall()` / search bumps `recall_count` on the returned
330
386
  rows so the next Stage-1 ranks them higher.
331
387
 
388
+ ### Cognitive sleep (v7)
389
+
390
+ `loop-memory cognitive-sleep [--apply]` runs an **auditable cleanup
391
+ pass** over the store:
392
+
393
+ - Surfaces contradictions between memories (e.g. "user prefers X"
394
+ vs. "user prefers Y") so you can resolve them in one click rather
395
+ than chasing them across sessions.
396
+ - Drops items that are below the configurable floor (`min_score`) and
397
+ older than the floor age.
398
+ - Merges near-duplicate memories (cosine ≥ 0.95 with `MergeableBy` rules).
399
+ - Emits a full audit row per action — visible in the Dashboard →
400
+ **Audit** tab and reachable via the MCP `audit` tool, so every
401
+ byte the consolidator ever touches is traceable.
402
+
403
+ Dry-run by default; pass `--apply` to commit.
404
+
405
+ ### Knowledge graph
406
+
407
+ `loop-memory graph-rebuild` extracts entities from every distilled
408
+ wiki page and every long-term memory, then materialises a typed
409
+ relation graph:
410
+
411
+ - Visible as the **Knowledge graph** globe tab in the web UI.
412
+ - Queryable via the MCP `subgraph` and `remember_edge` tools.
413
+ - Re-built by the Evolution Consolidator's Stage-5 evolution memo, so
414
+ the graph evolves alongside the wiki.
415
+
332
416
  ## Auto-feedback into every LLM client (反哺)
333
417
 
334
418
  Distilled knowledge is only useful if your LLM tools can actually
@@ -385,21 +469,6 @@ configuration, scheduling, language switching, and light/dark themes.
385
469
 
386
470
  ---
387
471
 
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
472
  ## Programmatic use
404
473
 
405
474
  ```python
@@ -453,26 +522,35 @@ Even though v0.2 is built around local storage, the original
453
522
  ```
454
523
  loop_memory/
455
524
  loop_memory/
525
+ cli/main.py # CLI entrypoint + COMMAND_HELP table + `--version`
526
+ ingest/ # Codex / Claude / Hermes / OpenClaw / generic loaders
527
+ wiki/ # distillation, classifier, scope auto-promotion
528
+ graph/ # entity extraction + knowledge-graph build
529
+ jobs/ # consolidate / evolve / cognitive-sleep / scheduler / contradiction / graph
530
+ llm/ # provider protocol + OpenAI / Anthropic / Ollama / rule-based
531
+ backends/ # embedding (hashing / sentence-transformers) + vector store (memory / chroma)
532
+ storage/ # SQLite-backed MemoryStore + migrations
533
+ privacy/ # <private> stripping + regex redaction
534
+ security/ # Keychain / 0600-file secrets wrapper
535
+ mcp/ # stdio JSON-RPC MCP server
536
+ serve/ # FastAPI app, watcher, web UI (Timeline / Dashboard / Wiki / Graph)
537
+ export/ # markdown + v7 portable bundle export/import
538
+ sdk.py # four-verb stable API: remember / recall / forget / feedback
539
+ sdk_extensions.py # optional high-level helpers (graph edges, wiki pages)
540
+ engine/loop.py # Retrieve → Generate → Reflect → Store loop
456
541
  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
542
  examples/demo.py # runnable, zero-API-key demo
473
543
  py.typed
474
- tests/ # 92 unit tests, zero deps
475
- docs/auto-capture.md # launchd / systemd / cron recipes
544
+ tests/ # 475 unit tests across memory / SDK / serve / CLI / scripts
545
+ docs/
546
+ auto-capture.md # launchd / systemd / cron recipes
547
+ architecture.md # layered view + 5-stage evolution pipeline
548
+ api.md # HTTP API reference
549
+ agent-memory-api.md # four-verb SDK / HTTP / MCP contract
550
+ universal-agent-memory.md # v7 graph memory + cognitive sleep + bundles
551
+ providers.md # LLM provider reference
552
+ settings.md # settings table + secrets file
553
+ weekly-research-automation.md # how the project auto-evolves from upstream research
476
554
  ```
477
555
 
478
556
  ---
@@ -554,6 +632,67 @@ classifier is local and makes no model or network request on a wiki write.
554
632
 
555
633
  ---
556
634
 
635
+ ## FAQ & troubleshooting
636
+
637
+ **Q: `pip install loop-memory` succeeds but `loop-memory serve` says `ModuleNotFoundError: No module named 'fastapi'`.**
638
+ A: `fastapi` is the optional `[serve]` extra. Install it explicitly:
639
+ `pip install 'loop-memory[serve]'` (or `'loop-memory[all]'` for everything).
640
+
641
+ **Q: My `~/.codex/sessions/` is empty / nothing appears in the UI.**
642
+ A: Run `loop-memory doctor` — it prints per-source paths, last-seen
643
+ mtime, and whether the watcher is running. Then check
644
+ `loop-memory hook --source codex --watch ~/.codex/sessions` is alive
645
+ in another shell (or via launchd — see [docs/auto-capture.md](docs/auto-capture.md)).
646
+
647
+ **Q: Distillation never finishes / wiki stays empty.**
648
+ A: You need an LLM provider configured. Open the web UI → ⚙ Model,
649
+ pick a provider, paste an API key, and click **Save**. Then either
650
+ wait for the scheduler or hit **� Run now**. Zero-deps installs ship
651
+ with a rule-based provider as a placeholder so the loop never blocks
652
+ on a missing key.
653
+
654
+ **Q: `loop-memory install-hooks` warns that the token file already exists.**
655
+ A: That's expected — it's idempotent. To force a rewrite, delete the
656
+ target files (`~/.codex/config.toml`, `~/.claude/mcp.json`,
657
+ `~/.hermes/mcp.json`) and re-run. The tool also refuses to touch
658
+ non-loop-memory config keys.
659
+
660
+ **Q: How big can the SQLite store get before I should worry?**
661
+ A: Practical floor: 100k memories / 10k wiki pages stays under ~80 MB
662
+ and `recall()` returns in <100 ms. The Evolution Consolidator is
663
+ designed to keep the wiki tight (~hundreds of pages) rather than let
664
+ it grow unbounded. Run `loop-memory cognitive-sleep --apply` weekly
665
+ to drop the long tail.
666
+
667
+ **Q: Can I sync the store across machines?**
668
+ A: The SQLite file is git-friendly and copy-friendly. The `MEMORY.md`
669
+ + graph + memories + metadata **bundle** (`loop-memory export
670
+ <dir>`) is a portable v7 artefact you can commit, share, or
671
+ back-up. There is no first-class sync daemon — by design — so the
672
+ local-first guarantee is never violated.
673
+
674
+ **Q: Is there a hosted / cloud version?**
675
+ A: No. Loop Memory is MIT-licensed and 100% local; the SQLite file
676
+ lives under `~/.loop_memory/`. The web UI is bound to loopback by
677
+ default; binding to `0.0.0.0` prints a security warning and requires
678
+ an auth token.
679
+
680
+ **Q: Where do secrets / API keys live?**
681
+ A: Two places. Provider keys you set in the **⚙ Model** UI are
682
+ written to `~/.loop_memory/secrets.json` (mode 0600) via the
683
+ `loop_memory.security.secrets` wrapper, which prefers the macOS
684
+ Keychain on Darwin and falls back to the encrypted file on Linux.
685
+ The auth token used by the web UI is hashed in the SQLite settings
686
+ table — never stored in plaintext.
687
+
688
+ **Q: I see "no version" / "package not found" on the PyPI badge.**
689
+ A: shields.io pulls from a separate data source that lags PyPI by a
690
+ few minutes after a new release. Re-publish the badge warmer step
691
+ in `.github/workflows/publish.yml` to force a refresh, or wait ~30
692
+ minutes for shields.io to catch up.
693
+
694
+ ---
695
+
557
696
  ## Run the tests
558
697
 
559
698
  ```bash
@@ -4,11 +4,14 @@
4
4
 
5
5
  # Loop Memory
6
6
 
7
- > **A general-purpose memory system for every AI agent you run locally.**
7
+ > **A general-purpose, local-first memory system that closes the agent loop for every AI agent you run.**
8
8
  >
9
- > Auto-captures every Codex / Claude / Hermes / OpenClaw conversation,
10
- > distils them into a tight wiki of stable knowledge, and lets the
11
- > agent recall what matters on demand.
9
+ > Point Loop Memory at any agent's transcript directory and it
10
+ > quietly catches every conversation, scores it, distils the long
11
+ > tail into a tight wiki of stable knowledge, and re-injects the
12
+ > relevant pieces into the next session. Out of the box: hooks for
13
+ > Codex, Claude, Hermes, and OpenClaw; an SDK + generic watcher
14
+ > CLI cover everything else.
12
15
 
13
16
  [![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)
14
17
  [![PyPI](https://img.shields.io/pypi/v/loop-memory.svg?style=flat-square)](https://pypi.org/project/loop-memory/)
@@ -19,14 +22,51 @@
19
22
 
20
23
  ---
21
24
 
25
+ > **What's new in 0.4.1** — CLI dispatch hardened so every subcommand's
26
+ > `--help` / `--version` exits 0 cleanly (no more `ValueError: unknown
27
+ > source: '--help'` on a zero-deps install). Added a static
28
+ > `COMMAND_HELP` table guarded by 10 new regression cases in
29
+ > `tests/test_cli_version.py`, plus GitHub `About` / README lead now
30
+ > surface for "agent loop" searches.
31
+ > [Full changelog →](CHANGELOG.md)
32
+
33
+ ---
34
+
35
+ ## Table of contents
36
+
37
+ - [What it does](#what-it-does)
38
+ - [Install](#install)
39
+ - [Quickstart](#quickstart)
40
+ - [Why Loop Memory vs. every other agent-memory project](#why-loop-memory-vs-every-other-agent-memory-project)
41
+ - [Architecture & docs](#architecture--docs)
42
+ - [After install: 30-second setup](#after-install-30-second-setup)
43
+ - [Auto-capture (after every conversation)](#auto-capture-after-every-conversation)
44
+ - [Dashboard + Evolution consolidator](#dashboard--evolution-consolidator-看板--进化式蒸馏)
45
+ - [Auto-feedback into every LLM client](#auto-feedback-into-every-llm-client-反哺)
46
+ - [Web UI](#web-ui)
47
+ - [Programmatic use](#programmatic-use)
48
+ - [The four-stage loop](#the-four-stage-loop)
49
+ - [Project layout](#project-layout)
50
+ - [Security & auth token](#security--auth-token)
51
+ - [Wiki scope auto-classification](#wiki-scope-auto-classification)
52
+ - [FAQ & troubleshooting](#faq--troubleshooting)
53
+ - [Run the tests](#run-the-tests)
54
+ - [Using distilled knowledge in your clients](#using-distilled-knowledge-in-your-clients)
55
+ - [License](#license)
56
+
57
+ ---
58
+
22
59
  ## What it does
23
60
 
24
61
  **Loop Memory** gives every agent you use a single, persistent brain
25
- that outlives any one conversation. Each agent (Codex CLI, Claude
26
- Code, Hermes, OpenClaw / clawx, …) drops its transcripts onto disk;
27
- Loop Memory quietly catches them, scores every fragment by *importance
28
- × recency × usage × feedback*, distils the long tail into a curated
29
- wiki, and re-injects the relevant pieces into the next session.
62
+ that outlives any one conversation. Any agent that drops transcripts
63
+ on disk — Codex CLI, Claude Code, Hermes, OpenClaw / clawx, Aider,
64
+ Cursor, works; the shipped hooks cover the popular ones and a
65
+ generic watcher CLI (`loop-memory hook --source <name> --watch <dir>`)
66
+ accepts anything else. Loop Memory quietly catches every fragment,
67
+ scores it by *importance × recency × usage × feedback*, distils the
68
+ long tail into a curated wiki, and re-injects the relevant pieces
69
+ into the next session.
30
70
 
31
71
  ```mermaid
32
72
  flowchart LR
@@ -58,6 +98,21 @@ via MCP / hooks]
58
98
 
59
99
  *One loop, many agents, one evolving wiki.*
60
100
 
101
+ **Supported agents:**
102
+
103
+ | Agent | Capture path | Hook shipped? | Notes |
104
+ | -------------- | ----------------------------------- | :-----------: | ----- |
105
+ | Codex CLI | `~/.codex/sessions/*.json` | ✅ | MCP + SessionStart auto-wired by `install-hooks` |
106
+ | Claude Code | `~/.claude/**/*.jsonl` | ✅ | MCP + SessionStart auto-wired by `install-hooks` |
107
+ | Hermes | `~/.hermes/**/*.jsonl` | ✅ | MCP + SessionStart auto-wired by `install-hooks` |
108
+ | OpenClaw/clawx | `~/.openclaw/agents/main/sessions` + `workspace/memory/*.md` | ✅ | watcher only (no MCP yet); `openclaw-setup` enables launchd |
109
+ | Anything else | any on-disk transcript dir | — | use `loop-memory hook --source <name> --watch <dir>` (see [docs/auto-capture.md](docs/auto-capture.md)) |
110
+
111
+ The shipped hooks are the four popular agents we maintain in-tree.
112
+ The generic watcher CLI is the supported extension point for every
113
+ other agent — Aider, Cursor, Copilot, Cline, Continue, Goose, your
114
+ own home-grown CLI, anything that drops JSON/JSONL on disk.
115
+
61
116
  ---
62
117
 
63
118
  ## Install
@@ -98,7 +153,7 @@ following — every other project we looked at lacks at least one:
98
153
 
99
154
  | Capability | **Loop Memory** | Mem0 v3 | Hindsight | OpenViking | A-MEM |
100
155
  | --- | --- | --- | --- | --- | --- |
101
- | Multi-source capture (Codex / Claude / Hermes / OpenClaw) | ✅ out of the box | ⚠ requires plugin per client | ⚠ hosted only | ⚠ SDK + companion app | ❌ |
156
+ | Multi-source capture (any agent with on-disk transcripts) | ✅ generic watcher CLI; hooks shipped for Codex / Claude / Hermes / OpenClaw | ⚠ requires plugin per client | ⚠ hosted only | ⚠ SDK + companion app | ❌ |
102
157
  | Local-first SQLite (zero external services) | ✅ | ❌ Postgres + Qdrant | ❌ Postgres + Qdrant | ⚠ file-system + cloud | ⚠ ChromaDB |
103
158
  | Hybrid recall: BM25 + semantic + entity (RRF) | ✅ | ✅ | ✅ | ✅ | ⚠ entity-only |
104
159
  | Temporal reasoning in retrieval (boost / suppress by date intent) | ✅ | ✅ | ❌ | ❌ | ❌ |
@@ -179,6 +234,7 @@ flavors:
179
234
  | Claude Code | `loop-memory hook --source claude --watch ~/.claude` |
180
235
  | Hermes | `loop-memory hook --source hermes --watch ~/.hermes` |
181
236
  | OpenClaw (clawx) | `loop-memory hook --source openclaw --watch ~/.openclaw/agents/main/sessions` — also ingests `workspace/memory/*.md` daily logs |
237
+ | Anything else (Aider, Cursor, Copilot, Cline, Continue, Goose, …) | `loop-memory hook --source <name> --watch <path/to/transcripts>` — see [docs/auto-capture.md](docs/auto-capture.md) for loader requirements |
182
238
 
183
239
  Three of these in a `tmux` session, or persisted via launchd, keeps
184
240
  your memory store fresh without any clicks. Run `loop-memory
@@ -285,6 +341,34 @@ User signals close the loop:
285
341
  - Every `recall()` / search bumps `recall_count` on the returned
286
342
  rows so the next Stage-1 ranks them higher.
287
343
 
344
+ ### Cognitive sleep (v7)
345
+
346
+ `loop-memory cognitive-sleep [--apply]` runs an **auditable cleanup
347
+ pass** over the store:
348
+
349
+ - Surfaces contradictions between memories (e.g. "user prefers X"
350
+ vs. "user prefers Y") so you can resolve them in one click rather
351
+ than chasing them across sessions.
352
+ - Drops items that are below the configurable floor (`min_score`) and
353
+ older than the floor age.
354
+ - Merges near-duplicate memories (cosine ≥ 0.95 with `MergeableBy` rules).
355
+ - Emits a full audit row per action — visible in the Dashboard →
356
+ **Audit** tab and reachable via the MCP `audit` tool, so every
357
+ byte the consolidator ever touches is traceable.
358
+
359
+ Dry-run by default; pass `--apply` to commit.
360
+
361
+ ### Knowledge graph
362
+
363
+ `loop-memory graph-rebuild` extracts entities from every distilled
364
+ wiki page and every long-term memory, then materialises a typed
365
+ relation graph:
366
+
367
+ - Visible as the **Knowledge graph** globe tab in the web UI.
368
+ - Queryable via the MCP `subgraph` and `remember_edge` tools.
369
+ - Re-built by the Evolution Consolidator's Stage-5 evolution memo, so
370
+ the graph evolves alongside the wiki.
371
+
288
372
  ## Auto-feedback into every LLM client (反哺)
289
373
 
290
374
  Distilled knowledge is only useful if your LLM tools can actually
@@ -341,21 +425,6 @@ configuration, scheduling, language switching, and light/dark themes.
341
425
 
342
426
  ---
343
427
 
344
- ## Time-weighted scoring
345
-
346
- Every memory carries a `score ∈ [0, 1]` recomputed from:
347
-
348
- ```
349
- score = 0.35 · importance + 0.65 · recency
350
- recency = ½ ^ (age / half_life)
351
- ```
352
-
353
- `half_life` defaults to 30 days, configurable via
354
- `consolidate(half_life_days=...)`. The UI shows the score as a
355
- percentage; use `?min_score=0.85` to see only high-relevance memories.
356
-
357
- ---
358
-
359
428
  ## Programmatic use
360
429
 
361
430
  ```python
@@ -409,26 +478,35 @@ Even though v0.2 is built around local storage, the original
409
478
  ```
410
479
  loop_memory/
411
480
  loop_memory/
481
+ cli/main.py # CLI entrypoint + COMMAND_HELP table + `--version`
482
+ ingest/ # Codex / Claude / Hermes / OpenClaw / generic loaders
483
+ wiki/ # distillation, classifier, scope auto-promotion
484
+ graph/ # entity extraction + knowledge-graph build
485
+ jobs/ # consolidate / evolve / cognitive-sleep / scheduler / contradiction / graph
486
+ llm/ # provider protocol + OpenAI / Anthropic / Ollama / rule-based
487
+ backends/ # embedding (hashing / sentence-transformers) + vector store (memory / chroma)
488
+ storage/ # SQLite-backed MemoryStore + migrations
489
+ privacy/ # <private> stripping + regex redaction
490
+ security/ # Keychain / 0600-file secrets wrapper
491
+ mcp/ # stdio JSON-RPC MCP server
492
+ serve/ # FastAPI app, watcher, web UI (Timeline / Dashboard / Wiki / Graph)
493
+ export/ # markdown + v7 portable bundle export/import
494
+ sdk.py # four-verb stable API: remember / recall / forget / feedback
495
+ sdk_extensions.py # optional high-level helpers (graph edges, wiki pages)
496
+ engine/loop.py # Retrieve → Generate → Reflect → Store loop
412
497
  memory/types.py # MemoryItem + 4 tiers
413
- backends/embedding.py # BaseEmbedder, HashingEmbedder, IdentityEmbedder
414
- backends/vector_store.py # VectorStore protocol + InMemory / Chroma
415
- backends/sentence_embedder.py # optional sentence-transformers
416
- llm/base.py # LLMClient protocol + EchoLLM + helpers
417
- llm/openai_adapter.py # optional OpenAI client
418
- engine/loop.py # the Retrieve → Generate → Reflect → Store loop
419
- engine/reflect.py # reflection & summarization passes
420
- storage/sqlite_store.py # persistent SQLite-backed MemoryStore
421
- ingest/loader.py # CodexLoader, ClaudeLoader, HermesLoader
422
- ingest/pipeline.py # session → MemoryStore
423
- jobs/consolidate.py # background rescore + GC + dedupe
424
- serve/app.py # FastAPI app for the local web UI
425
- serve/static/index.html # the page
426
- serve/watcher.py # filesystem watcher for auto-capture
427
- cli/main.py # CLI entrypoint (chat / stats / ingest / consolidate / serve / hook)
428
498
  examples/demo.py # runnable, zero-API-key demo
429
499
  py.typed
430
- tests/ # 92 unit tests, zero deps
431
- docs/auto-capture.md # launchd / systemd / cron recipes
500
+ tests/ # 475 unit tests across memory / SDK / serve / CLI / scripts
501
+ docs/
502
+ auto-capture.md # launchd / systemd / cron recipes
503
+ architecture.md # layered view + 5-stage evolution pipeline
504
+ api.md # HTTP API reference
505
+ agent-memory-api.md # four-verb SDK / HTTP / MCP contract
506
+ universal-agent-memory.md # v7 graph memory + cognitive sleep + bundles
507
+ providers.md # LLM provider reference
508
+ settings.md # settings table + secrets file
509
+ weekly-research-automation.md # how the project auto-evolves from upstream research
432
510
  ```
433
511
 
434
512
  ---
@@ -510,6 +588,67 @@ classifier is local and makes no model or network request on a wiki write.
510
588
 
511
589
  ---
512
590
 
591
+ ## FAQ & troubleshooting
592
+
593
+ **Q: `pip install loop-memory` succeeds but `loop-memory serve` says `ModuleNotFoundError: No module named 'fastapi'`.**
594
+ A: `fastapi` is the optional `[serve]` extra. Install it explicitly:
595
+ `pip install 'loop-memory[serve]'` (or `'loop-memory[all]'` for everything).
596
+
597
+ **Q: My `~/.codex/sessions/` is empty / nothing appears in the UI.**
598
+ A: Run `loop-memory doctor` — it prints per-source paths, last-seen
599
+ mtime, and whether the watcher is running. Then check
600
+ `loop-memory hook --source codex --watch ~/.codex/sessions` is alive
601
+ in another shell (or via launchd — see [docs/auto-capture.md](docs/auto-capture.md)).
602
+
603
+ **Q: Distillation never finishes / wiki stays empty.**
604
+ A: You need an LLM provider configured. Open the web UI → ⚙ Model,
605
+ pick a provider, paste an API key, and click **Save**. Then either
606
+ wait for the scheduler or hit **� Run now**. Zero-deps installs ship
607
+ with a rule-based provider as a placeholder so the loop never blocks
608
+ on a missing key.
609
+
610
+ **Q: `loop-memory install-hooks` warns that the token file already exists.**
611
+ A: That's expected — it's idempotent. To force a rewrite, delete the
612
+ target files (`~/.codex/config.toml`, `~/.claude/mcp.json`,
613
+ `~/.hermes/mcp.json`) and re-run. The tool also refuses to touch
614
+ non-loop-memory config keys.
615
+
616
+ **Q: How big can the SQLite store get before I should worry?**
617
+ A: Practical floor: 100k memories / 10k wiki pages stays under ~80 MB
618
+ and `recall()` returns in <100 ms. The Evolution Consolidator is
619
+ designed to keep the wiki tight (~hundreds of pages) rather than let
620
+ it grow unbounded. Run `loop-memory cognitive-sleep --apply` weekly
621
+ to drop the long tail.
622
+
623
+ **Q: Can I sync the store across machines?**
624
+ A: The SQLite file is git-friendly and copy-friendly. The `MEMORY.md`
625
+ + graph + memories + metadata **bundle** (`loop-memory export
626
+ <dir>`) is a portable v7 artefact you can commit, share, or
627
+ back-up. There is no first-class sync daemon — by design — so the
628
+ local-first guarantee is never violated.
629
+
630
+ **Q: Is there a hosted / cloud version?**
631
+ A: No. Loop Memory is MIT-licensed and 100% local; the SQLite file
632
+ lives under `~/.loop_memory/`. The web UI is bound to loopback by
633
+ default; binding to `0.0.0.0` prints a security warning and requires
634
+ an auth token.
635
+
636
+ **Q: Where do secrets / API keys live?**
637
+ A: Two places. Provider keys you set in the **⚙ Model** UI are
638
+ written to `~/.loop_memory/secrets.json` (mode 0600) via the
639
+ `loop_memory.security.secrets` wrapper, which prefers the macOS
640
+ Keychain on Darwin and falls back to the encrypted file on Linux.
641
+ The auth token used by the web UI is hashed in the SQLite settings
642
+ table — never stored in plaintext.
643
+
644
+ **Q: I see "no version" / "package not found" on the PyPI badge.**
645
+ A: shields.io pulls from a separate data source that lags PyPI by a
646
+ few minutes after a new release. Re-publish the badge warmer step
647
+ in `.github/workflows/publish.yml` to force a refresh, or wait ~30
648
+ minutes for shields.io to catch up.
649
+
650
+ ---
651
+
513
652
  ## Run the tests
514
653
 
515
654
  ```bash