graph-mind-memory 0.5.1__tar.gz → 0.5.3__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 (66) hide show
  1. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3}/PKG-INFO +24 -20
  2. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3}/README.md +22 -19
  3. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3}/pyproject.toml +4 -2
  4. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/conversation_memory.py +9 -0
  5. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/graph_mind_mcp_server.py +21 -11
  6. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/graph_mind_memory.egg-info/PKG-INFO +24 -20
  7. graph_mind_memory-0.5.3/src/graph_mind_memory.egg-info/SOURCES.txt +63 -0
  8. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/install.py +21 -15
  9. graph_mind_memory-0.5.3/tests/test_associative_memory.py +120 -0
  10. graph_mind_memory-0.5.3/tests/test_automatic_capture.py +101 -0
  11. graph_mind_memory-0.5.3/tests/test_capture_health.py +36 -0
  12. graph_mind_memory-0.5.3/tests/test_coding_memory.py +75 -0
  13. graph_mind_memory-0.5.3/tests/test_cold_start.py +77 -0
  14. graph_mind_memory-0.5.3/tests/test_concurrent_use.py +40 -0
  15. graph_mind_memory-0.5.3/tests/test_conversation_memory.py +191 -0
  16. graph_mind_memory-0.5.3/tests/test_executable_memory.py +105 -0
  17. graph_mind_memory-0.5.3/tests/test_executable_plan_v2_diagnostic.py +52 -0
  18. graph_mind_memory-0.5.3/tests/test_forget.py +125 -0
  19. graph_mind_memory-0.5.3/tests/test_forget_conversation.py +100 -0
  20. graph_mind_memory-0.5.3/tests/test_graph_mind_mcp_server.py +118 -0
  21. graph_mind_memory-0.5.3/tests/test_ingestion_gate.py +67 -0
  22. graph_mind_memory-0.5.3/tests/test_ingestion_synonyms.py +70 -0
  23. graph_mind_memory-0.5.3/tests/test_install.py +160 -0
  24. graph_mind_memory-0.5.3/tests/test_llm_concept_planner.py +87 -0
  25. graph_mind_memory-0.5.3/tests/test_local_brain.py +60 -0
  26. graph_mind_memory-0.5.3/tests/test_longmemeval_adapter.py +49 -0
  27. graph_mind_memory-0.5.3/tests/test_longmemeval_ingestion_calibration.py +45 -0
  28. graph_mind_memory-0.5.3/tests/test_longmemeval_ingestion_preflight.py +22 -0
  29. graph_mind_memory-0.5.3/tests/test_longmemeval_ingestion_v7.py +55 -0
  30. graph_mind_memory-0.5.3/tests/test_memory_algebra.py +45 -0
  31. graph_mind_memory-0.5.3/tests/test_precondition_verifier.py +39 -0
  32. graph_mind_memory-0.5.3/tests/test_recent_recall.py +115 -0
  33. graph_mind_memory-0.5.3/tests/test_resume_retries.py +26 -0
  34. graph_mind_memory-0.5.3/tests/test_secret_masking.py +76 -0
  35. graph_mind_memory-0.5.3/tests/test_semantic_llm_extractor.py +102 -0
  36. graph_mind_memory-0.5.3/tests/test_semantic_memory.py +69 -0
  37. graph_mind_memory-0.5.3/tests/test_semantic_recall.py +89 -0
  38. graph_mind_memory-0.5.3/tests/test_shared_brain.py +269 -0
  39. graph_mind_memory-0.5.3/tests/test_temporal_state.py +151 -0
  40. graph_mind_memory-0.5.3/tests/test_universal_personal_memory.py +73 -0
  41. graph_mind_memory-0.5.1/graph_mind_memory.egg-info/SOURCES.txt +0 -31
  42. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3}/LICENSE +0 -0
  43. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3}/setup.cfg +0 -0
  44. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/associative_memory.py +0 -0
  45. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/automatic_capture.py +0 -0
  46. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/automatic_capture_cli.py +0 -0
  47. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/brain_log.py +0 -0
  48. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/clock.py +0 -0
  49. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/coding_memory.py +0 -0
  50. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/development_memory.py +0 -0
  51. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/development_paths.py +0 -0
  52. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/embedding_warmup.py +0 -0
  53. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/entity_timeline.py +0 -0
  54. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/forget.py +0 -0
  55. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/graph_mind_memory.egg-info/dependency_links.txt +0 -0
  56. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/graph_mind_memory.egg-info/entry_points.txt +0 -0
  57. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/graph_mind_memory.egg-info/requires.txt +0 -0
  58. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/graph_mind_memory.egg-info/top_level.txt +0 -0
  59. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/local_brain.py +0 -0
  60. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/local_embedder.py +0 -0
  61. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/semantic_llm_extractor.py +0 -0
  62. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/semantic_memory.py +0 -0
  63. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/semantic_recall.py +0 -0
  64. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/temporal_resolver.py +0 -0
  65. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/universal_personal_memory.py +0 -0
  66. {graph_mind_memory-0.5.1 → graph_mind_memory-0.5.3/src}/vector_cache.py +0 -0
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graph-mind-memory
3
- Version: 0.5.1
3
+ Version: 0.5.3
4
4
  Summary: Local-first memory for AI coding assistants: verbatim, on your own PC, no LLM calls at write time.
5
5
  Author-email: Yohan Ko <goyohan0611@gmail.com>
6
6
  License-Expression: AGPL-3.0-only
7
7
  Project-URL: Homepage, https://github.com/goyohan0611-png/graph-mind
8
8
  Project-URL: Issues, https://github.com/goyohan0611-png/graph-mind/issues
9
+ Project-URL: Changelog, https://github.com/goyohan0611-png/graph-mind/blob/main/CHANGELOG.md
9
10
  Keywords: mcp,memory,claude,codex,llm,longmemeval,local-first
10
11
  Classifier: Development Status :: 3 - Alpha
11
12
  Classifier: Programming Language :: Python :: 3
@@ -77,8 +78,8 @@ calls Graph-MIND over MCP and gets back a few thousand tokens of the conversatio
77
78
  ## Benchmarks
78
79
 
79
80
  All numbers below come from files in this repository: the pre-registrations, each question's
80
- answer, and the judge's verdict on it, under [`runs/`](runs). The method is in
81
- [REPORT.md](REPORT.md), including the failures and the corrections.
81
+ answer, and the judge's verdict on it, under [`research/runs/`](research/runs). The method is in
82
+ [REPORT.md](research/REPORT.md), including the failures and the corrections.
82
83
 
83
84
  **LongMemEval_S: answer accuracy, 500 questions.** The answer model is gpt-5-mini; the judge is the
84
85
  official gpt-4o-2024-08-06.
@@ -120,6 +121,8 @@ Requires Python 3.10+ (64-bit). Windows, macOS or Linux. Hosting a brain that ot
120
121
  needs Python 3.12 or older on that one PC (its Postgres helper, pgserver, has no newer build yet);
121
122
  everything else, joining included, works on 3.13 and 3.14 too.
122
123
 
124
+ What changed in each release: [CHANGELOG.md](CHANGELOG.md).
125
+
123
126
  ```bash
124
127
  pip install graph-mind-memory
125
128
  graph-mind-install
@@ -136,7 +139,7 @@ or from source:
136
139
  ```bash
137
140
  git clone https://github.com/goyohan0611-png/graph-mind.git
138
141
  cd graph-mind
139
- python install.py
142
+ python src/install.py
140
143
  ```
141
144
 
142
145
  The installer:
@@ -180,9 +183,12 @@ random 20-64 character tokens caught; in 246,750 ordinary chat turns it fired 92
180
183
  all on real ids and tokens). Commit ids, hashes and UUIDs are kept, since you recall those on
181
184
  purpose.
182
185
 
183
- There is no switch to keep keys: a recalled memory goes to your model's provider with the question,
184
- so a stored key would leave your PC the first time it is useful. To have Graph-MIND remember a key,
185
- tell it **where the key is**, not the key: *"my OpenAI key is in 1Password, under Dev"*.
186
+ Keep masking on if you use a cloud model: a recalled memory goes to your model's provider with the
187
+ question, so a stored key would leave your PC the first time it is useful. To have Graph-MIND
188
+ remember a key, tell it **where the key is**, not the key: *"my OpenAI key is in 1Password, under
189
+ Dev"*. If your model runs on your own machines and you want keys remembered as typed, add
190
+ `"mask_secrets": false` to `~/.graph-mind/config.json` (applies from the next message; already
191
+ masked text stays masked).
186
192
 
187
193
  Only what you actually send is captured: the service reads each app's transcript, which is written
188
194
  after you press Enter, so a paste you delete before sending never reaches it. A plain password
@@ -227,7 +233,7 @@ On the PC that holds the memory, tell its AI:
227
233
  It replies with a connection code (`gm1.…`). On each other PC:
228
234
 
229
235
  ```bash
230
- graph-mind-install --join gm1.… # or: python install.py --join gm1.…
236
+ graph-mind-install --join gm1.… # or: python src/install.py --join gm1.…
231
237
  ```
232
238
 
233
239
  The memory then lives in a Postgres server on the first PC, which Graph-MIND sets up itself. Other
@@ -248,16 +254,17 @@ each PC.
248
254
  ## Reproducing the benchmarks
249
255
 
250
256
  1. Download `longmemeval_s_cleaned.json` from [LongMemEval](https://github.com/xiaowu0162/LongMemEval)
251
- into `external/longmemeval/`.
257
+ into `research/external/longmemeval/`.
252
258
  2. Set `OPENAI_API_KEY` for the answer model and the judge.
253
- 3. Run the commands in [REPORT.md §10](REPORT.md#10-reproducing).
259
+ 3. `pip install -e .`, then from `research/` run the commands in
260
+ [REPORT.md §10](research/REPORT.md#10-reproducing).
254
261
 
255
262
  A full 500-question run costs about US$3.
256
263
 
257
264
  ## Tests
258
265
 
259
266
  ```bash
260
- python -m unittest discover -p "test_*.py"
267
+ python -m unittest discover -s tests -p "test_*.py"
261
268
  ```
262
269
 
263
270
  ---
@@ -273,20 +280,17 @@ python -m unittest discover -p "test_*.py"
273
280
  can stop capture until Graph-MIND is updated.
274
281
  - **Multi-session questions are the weakest type at 80%.** These are questions that count or
275
282
  combine facts across many conversations.
276
- - **The repository still holds the research-phase experiments** next to the product (see
277
- [Repository layout](#repository-layout)); the installed package carries only the 22 product
278
- modules.
279
283
 
280
- The full list is in [REPORT.md §9](REPORT.md#9-known-limits).
284
+ The full list is in [REPORT.md §9](research/REPORT.md#9-known-limits).
281
285
 
282
286
  ## Repository layout
283
287
 
284
- | | files |
288
+ | folder | what is in it |
285
289
  |---|---|
286
- | **Product** (what `pip install graph-mind-memory` installs) | `graph_mind_mcp_server.py` (MCP server), `automatic_capture*.py` (capture service), `install.py`, `brain_log.py` (sharing across PCs), `forget.py` (deleting), `local_brain.py` / `conversation_memory.py` / `coding_memory.py` / `development_memory.py` (stores), `semantic_recall.py` / `local_embedder.py` / `vector_cache.py` / `embedding_warmup.py` (search), and their helpers |
287
- | **Benchmarks** | `product_answer_eval.py`, `official_judge_v073.py`, `rival_mem0.py`, `rival_mempalace.py`, `rival_clean_prereg.py`, and the result files under `runs/` |
288
- | **Research phase** | the other modules: earlier extraction pipelines and analyses that REPORT.md cites |
289
- | **Tests** | `test_*.py` |
290
+ | [`src/`](src) | the product, exactly what `pip install graph-mind-memory` installs: `graph_mind_mcp_server.py` (MCP server), `automatic_capture*.py` (capture service), `install.py`, `brain_log.py` (sharing across PCs), `forget.py` (deleting), the stores, and search (`semantic_recall.py`, `local_embedder.py`) |
291
+ | [`tests/`](tests) | unit tests, run in CI on Windows, Linux and macOS |
292
+ | [`research/`](research) | the benchmark and research-phase scripts, [REPORT.md](research/REPORT.md), and every run's results under `runs/` |
293
+ | [`demo/`](demo) | the README's GIF and the script that records it |
290
294
 
291
295
  ## Security
292
296
 
@@ -50,8 +50,8 @@ calls Graph-MIND over MCP and gets back a few thousand tokens of the conversatio
50
50
  ## Benchmarks
51
51
 
52
52
  All numbers below come from files in this repository: the pre-registrations, each question's
53
- answer, and the judge's verdict on it, under [`runs/`](runs). The method is in
54
- [REPORT.md](REPORT.md), including the failures and the corrections.
53
+ answer, and the judge's verdict on it, under [`research/runs/`](research/runs). The method is in
54
+ [REPORT.md](research/REPORT.md), including the failures and the corrections.
55
55
 
56
56
  **LongMemEval_S: answer accuracy, 500 questions.** The answer model is gpt-5-mini; the judge is the
57
57
  official gpt-4o-2024-08-06.
@@ -93,6 +93,8 @@ Requires Python 3.10+ (64-bit). Windows, macOS or Linux. Hosting a brain that ot
93
93
  needs Python 3.12 or older on that one PC (its Postgres helper, pgserver, has no newer build yet);
94
94
  everything else, joining included, works on 3.13 and 3.14 too.
95
95
 
96
+ What changed in each release: [CHANGELOG.md](CHANGELOG.md).
97
+
96
98
  ```bash
97
99
  pip install graph-mind-memory
98
100
  graph-mind-install
@@ -109,7 +111,7 @@ or from source:
109
111
  ```bash
110
112
  git clone https://github.com/goyohan0611-png/graph-mind.git
111
113
  cd graph-mind
112
- python install.py
114
+ python src/install.py
113
115
  ```
114
116
 
115
117
  The installer:
@@ -153,9 +155,12 @@ random 20-64 character tokens caught; in 246,750 ordinary chat turns it fired 92
153
155
  all on real ids and tokens). Commit ids, hashes and UUIDs are kept, since you recall those on
154
156
  purpose.
155
157
 
156
- There is no switch to keep keys: a recalled memory goes to your model's provider with the question,
157
- so a stored key would leave your PC the first time it is useful. To have Graph-MIND remember a key,
158
- tell it **where the key is**, not the key: *"my OpenAI key is in 1Password, under Dev"*.
158
+ Keep masking on if you use a cloud model: a recalled memory goes to your model's provider with the
159
+ question, so a stored key would leave your PC the first time it is useful. To have Graph-MIND
160
+ remember a key, tell it **where the key is**, not the key: *"my OpenAI key is in 1Password, under
161
+ Dev"*. If your model runs on your own machines and you want keys remembered as typed, add
162
+ `"mask_secrets": false` to `~/.graph-mind/config.json` (applies from the next message; already
163
+ masked text stays masked).
159
164
 
160
165
  Only what you actually send is captured: the service reads each app's transcript, which is written
161
166
  after you press Enter, so a paste you delete before sending never reaches it. A plain password
@@ -200,7 +205,7 @@ On the PC that holds the memory, tell its AI:
200
205
  It replies with a connection code (`gm1.…`). On each other PC:
201
206
 
202
207
  ```bash
203
- graph-mind-install --join gm1.… # or: python install.py --join gm1.…
208
+ graph-mind-install --join gm1.… # or: python src/install.py --join gm1.…
204
209
  ```
205
210
 
206
211
  The memory then lives in a Postgres server on the first PC, which Graph-MIND sets up itself. Other
@@ -221,16 +226,17 @@ each PC.
221
226
  ## Reproducing the benchmarks
222
227
 
223
228
  1. Download `longmemeval_s_cleaned.json` from [LongMemEval](https://github.com/xiaowu0162/LongMemEval)
224
- into `external/longmemeval/`.
229
+ into `research/external/longmemeval/`.
225
230
  2. Set `OPENAI_API_KEY` for the answer model and the judge.
226
- 3. Run the commands in [REPORT.md §10](REPORT.md#10-reproducing).
231
+ 3. `pip install -e .`, then from `research/` run the commands in
232
+ [REPORT.md §10](research/REPORT.md#10-reproducing).
227
233
 
228
234
  A full 500-question run costs about US$3.
229
235
 
230
236
  ## Tests
231
237
 
232
238
  ```bash
233
- python -m unittest discover -p "test_*.py"
239
+ python -m unittest discover -s tests -p "test_*.py"
234
240
  ```
235
241
 
236
242
  ---
@@ -246,20 +252,17 @@ python -m unittest discover -p "test_*.py"
246
252
  can stop capture until Graph-MIND is updated.
247
253
  - **Multi-session questions are the weakest type at 80%.** These are questions that count or
248
254
  combine facts across many conversations.
249
- - **The repository still holds the research-phase experiments** next to the product (see
250
- [Repository layout](#repository-layout)); the installed package carries only the 22 product
251
- modules.
252
255
 
253
- The full list is in [REPORT.md §9](REPORT.md#9-known-limits).
256
+ The full list is in [REPORT.md §9](research/REPORT.md#9-known-limits).
254
257
 
255
258
  ## Repository layout
256
259
 
257
- | | files |
260
+ | folder | what is in it |
258
261
  |---|---|
259
- | **Product** (what `pip install graph-mind-memory` installs) | `graph_mind_mcp_server.py` (MCP server), `automatic_capture*.py` (capture service), `install.py`, `brain_log.py` (sharing across PCs), `forget.py` (deleting), `local_brain.py` / `conversation_memory.py` / `coding_memory.py` / `development_memory.py` (stores), `semantic_recall.py` / `local_embedder.py` / `vector_cache.py` / `embedding_warmup.py` (search), and their helpers |
260
- | **Benchmarks** | `product_answer_eval.py`, `official_judge_v073.py`, `rival_mem0.py`, `rival_mempalace.py`, `rival_clean_prereg.py`, and the result files under `runs/` |
261
- | **Research phase** | the other modules: earlier extraction pipelines and analyses that REPORT.md cites |
262
- | **Tests** | `test_*.py` |
262
+ | [`src/`](src) | the product, exactly what `pip install graph-mind-memory` installs: `graph_mind_mcp_server.py` (MCP server), `automatic_capture*.py` (capture service), `install.py`, `brain_log.py` (sharing across PCs), `forget.py` (deleting), the stores, and search (`semantic_recall.py`, `local_embedder.py`) |
263
+ | [`tests/`](tests) | unit tests, run in CI on Windows, Linux and macOS |
264
+ | [`research/`](research) | the benchmark and research-phase scripts, [REPORT.md](research/REPORT.md), and every run's results under `runs/` |
265
+ | [`demo/`](demo) | the README's GIF and the script that records it |
263
266
 
264
267
  ## Security
265
268
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "graph-mind-memory"
7
- version = "0.5.1"
7
+ version = "0.5.3"
8
8
  description = "Local-first memory for AI coding assistants: verbatim, on your own PC, no LLM calls at write time."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -35,6 +35,7 @@ dependencies = [
35
35
  [project.urls]
36
36
  Homepage = "https://github.com/goyohan0611-png/graph-mind"
37
37
  Issues = "https://github.com/goyohan0611-png/graph-mind/issues"
38
+ Changelog = "https://github.com/goyohan0611-png/graph-mind/blob/main/CHANGELOG.md"
38
39
 
39
40
  [project.scripts]
40
41
  graph-mind = "graph_mind_mcp_server:main"
@@ -44,7 +45,8 @@ graph-mind-capture = "automatic_capture_cli:main"
44
45
  graph-mind-forget = "forget:main"
45
46
 
46
47
  [tool.setuptools]
47
- # ponytail: flat modules, as in the repository; a graph_mind/ package is the upgrade path.
48
+ # ponytail: flat modules under src/; a graph_mind/ package is the upgrade path.
49
+ package-dir = { "" = "src" }
48
50
  py-modules = [
49
51
  "associative_memory",
50
52
  "automatic_capture",
@@ -79,8 +79,17 @@ AGENT_INSTRUCTIONS = re.compile(
79
79
  r"(?:#\s*AGENTS\.md instructions[^\n]*\s*)?<INSTRUCTIONS>.*?</INSTRUCTIONS>", flags=re.DOTALL)
80
80
 
81
81
 
82
+ def masking_on():
83
+ """On unless ~/.graph-mind/config.json says "mask_secrets": false: someone running a model on
84
+ their own machines may want keys kept in memory verbatim."""
85
+ from brain_log import _config
86
+ return _config().get("mask_secrets", True) is not False
87
+
88
+
82
89
  def _redact(value):
83
90
  cleaned = AGENT_INSTRUCTIONS.sub("", AUTOMATIC_BLOCKS.sub("", value))
91
+ if not masking_on():
92
+ return cleaned.strip(), 0
84
93
  count = 0
85
94
  for pattern, replacement in SECRET_PATTERNS:
86
95
  cleaned, replacements = pattern.subn(replacement, cleaned)
@@ -26,12 +26,13 @@ if hasattr(sys.stderr, "reconfigure"):
26
26
 
27
27
 
28
28
  def _version() -> str:
29
- """The package version: pyproject.toml beside this file in a source checkout, else the
29
+ """The package version: the checkout's pyproject.toml (one folder up from src/), else the
30
30
  installed distribution's. A hand-kept constant here had drifted to 0.5.0 at release 0.3.1."""
31
31
  import re
32
- project = Path(__file__).with_name("pyproject.toml")
33
- if project.exists():
34
- found = re.search(r'^version = "([^"]+)"', project.read_text(encoding="utf-8"), re.M)
32
+ project = Path(__file__).resolve().parent.parent / "pyproject.toml"
33
+ text = project.read_text(encoding="utf-8") if project.exists() else ""
34
+ if 'name = "graph-mind-memory"' in text:
35
+ found = re.search(r'^version = "([^"]+)"', text, re.M)
35
36
  if found:
36
37
  return found.group(1)
37
38
  try:
@@ -358,6 +359,21 @@ def capture_warnings() -> list[str]:
358
359
  return []
359
360
 
360
361
 
362
+ PROACTIVE_NOTE = (
363
+ "Remember proactively. Whenever the user mentions something about their life, work, "
364
+ "plans, preferences, people, places, meals, purchases, health, decisions or events, "
365
+ "call brain_remember right away with their own words, without asking first and "
366
+ "without waiting to be told to remember it. Saving too much costs nothing; forgetting "
367
+ "is what the user notices. Do it silently: never tell the user that you saved, "
368
+ "stored or noted something, and do not add lines like 'I saved that to memory'. "
369
+ "Mention memory only when the user asks what you remember or whether it was saved. ")
370
+ # set by the installer for apps whose transcripts the capture service already saves
371
+ CAPTURED_NOTE = (
372
+ "Every turn of this conversation is saved automatically. Do not call brain_remember for "
373
+ "ordinary chat; call it only when the user explicitly asks you to remember something or "
374
+ "to record a decision. ")
375
+
376
+
361
377
  def build_server(db_path=None):
362
378
  database = Path(db_path).expanduser() if db_path is not None else configured_db_path()
363
379
  server = MCPServer(
@@ -365,13 +381,7 @@ def build_server(db_path=None):
365
381
  title="Graph-MIND Universal Personal Memory",
366
382
  description="User-owned local brain shared across models, sessions and applications.",
367
383
  instructions=(
368
- "Remember proactively. Whenever the user mentions something about their life, work, "
369
- "plans, preferences, people, places, meals, purchases, health, decisions or events, "
370
- "call brain_remember right away with their own words, without asking first and "
371
- "without waiting to be told to remember it. Saving too much costs nothing; forgetting "
372
- "is what the user notices. Do it silently: never tell the user that you saved, "
373
- "stored or noted something, and do not add lines like 'I saved that to memory'. "
374
- "Mention memory only when the user asks what you remember or whether it was saved. "
384
+ (CAPTURED_NOTE if os.environ.get("GRAPH_MIND_CAPTURED") else PROACTIVE_NOTE) +
375
385
  "Call brain_context first when a request may depend on the user's prior conversations, "
376
386
  "facts, preferences, decisions, projects or work, including 'where did we leave off?'. "
377
387
  "It skips unrelated requests and returns a bounded evidence packet instead of "
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graph-mind-memory
3
- Version: 0.5.1
3
+ Version: 0.5.3
4
4
  Summary: Local-first memory for AI coding assistants: verbatim, on your own PC, no LLM calls at write time.
5
5
  Author-email: Yohan Ko <goyohan0611@gmail.com>
6
6
  License-Expression: AGPL-3.0-only
7
7
  Project-URL: Homepage, https://github.com/goyohan0611-png/graph-mind
8
8
  Project-URL: Issues, https://github.com/goyohan0611-png/graph-mind/issues
9
+ Project-URL: Changelog, https://github.com/goyohan0611-png/graph-mind/blob/main/CHANGELOG.md
9
10
  Keywords: mcp,memory,claude,codex,llm,longmemeval,local-first
10
11
  Classifier: Development Status :: 3 - Alpha
11
12
  Classifier: Programming Language :: Python :: 3
@@ -77,8 +78,8 @@ calls Graph-MIND over MCP and gets back a few thousand tokens of the conversatio
77
78
  ## Benchmarks
78
79
 
79
80
  All numbers below come from files in this repository: the pre-registrations, each question's
80
- answer, and the judge's verdict on it, under [`runs/`](runs). The method is in
81
- [REPORT.md](REPORT.md), including the failures and the corrections.
81
+ answer, and the judge's verdict on it, under [`research/runs/`](research/runs). The method is in
82
+ [REPORT.md](research/REPORT.md), including the failures and the corrections.
82
83
 
83
84
  **LongMemEval_S: answer accuracy, 500 questions.** The answer model is gpt-5-mini; the judge is the
84
85
  official gpt-4o-2024-08-06.
@@ -120,6 +121,8 @@ Requires Python 3.10+ (64-bit). Windows, macOS or Linux. Hosting a brain that ot
120
121
  needs Python 3.12 or older on that one PC (its Postgres helper, pgserver, has no newer build yet);
121
122
  everything else, joining included, works on 3.13 and 3.14 too.
122
123
 
124
+ What changed in each release: [CHANGELOG.md](CHANGELOG.md).
125
+
123
126
  ```bash
124
127
  pip install graph-mind-memory
125
128
  graph-mind-install
@@ -136,7 +139,7 @@ or from source:
136
139
  ```bash
137
140
  git clone https://github.com/goyohan0611-png/graph-mind.git
138
141
  cd graph-mind
139
- python install.py
142
+ python src/install.py
140
143
  ```
141
144
 
142
145
  The installer:
@@ -180,9 +183,12 @@ random 20-64 character tokens caught; in 246,750 ordinary chat turns it fired 92
180
183
  all on real ids and tokens). Commit ids, hashes and UUIDs are kept, since you recall those on
181
184
  purpose.
182
185
 
183
- There is no switch to keep keys: a recalled memory goes to your model's provider with the question,
184
- so a stored key would leave your PC the first time it is useful. To have Graph-MIND remember a key,
185
- tell it **where the key is**, not the key: *"my OpenAI key is in 1Password, under Dev"*.
186
+ Keep masking on if you use a cloud model: a recalled memory goes to your model's provider with the
187
+ question, so a stored key would leave your PC the first time it is useful. To have Graph-MIND
188
+ remember a key, tell it **where the key is**, not the key: *"my OpenAI key is in 1Password, under
189
+ Dev"*. If your model runs on your own machines and you want keys remembered as typed, add
190
+ `"mask_secrets": false` to `~/.graph-mind/config.json` (applies from the next message; already
191
+ masked text stays masked).
186
192
 
187
193
  Only what you actually send is captured: the service reads each app's transcript, which is written
188
194
  after you press Enter, so a paste you delete before sending never reaches it. A plain password
@@ -227,7 +233,7 @@ On the PC that holds the memory, tell its AI:
227
233
  It replies with a connection code (`gm1.…`). On each other PC:
228
234
 
229
235
  ```bash
230
- graph-mind-install --join gm1.… # or: python install.py --join gm1.…
236
+ graph-mind-install --join gm1.… # or: python src/install.py --join gm1.…
231
237
  ```
232
238
 
233
239
  The memory then lives in a Postgres server on the first PC, which Graph-MIND sets up itself. Other
@@ -248,16 +254,17 @@ each PC.
248
254
  ## Reproducing the benchmarks
249
255
 
250
256
  1. Download `longmemeval_s_cleaned.json` from [LongMemEval](https://github.com/xiaowu0162/LongMemEval)
251
- into `external/longmemeval/`.
257
+ into `research/external/longmemeval/`.
252
258
  2. Set `OPENAI_API_KEY` for the answer model and the judge.
253
- 3. Run the commands in [REPORT.md §10](REPORT.md#10-reproducing).
259
+ 3. `pip install -e .`, then from `research/` run the commands in
260
+ [REPORT.md §10](research/REPORT.md#10-reproducing).
254
261
 
255
262
  A full 500-question run costs about US$3.
256
263
 
257
264
  ## Tests
258
265
 
259
266
  ```bash
260
- python -m unittest discover -p "test_*.py"
267
+ python -m unittest discover -s tests -p "test_*.py"
261
268
  ```
262
269
 
263
270
  ---
@@ -273,20 +280,17 @@ python -m unittest discover -p "test_*.py"
273
280
  can stop capture until Graph-MIND is updated.
274
281
  - **Multi-session questions are the weakest type at 80%.** These are questions that count or
275
282
  combine facts across many conversations.
276
- - **The repository still holds the research-phase experiments** next to the product (see
277
- [Repository layout](#repository-layout)); the installed package carries only the 22 product
278
- modules.
279
283
 
280
- The full list is in [REPORT.md §9](REPORT.md#9-known-limits).
284
+ The full list is in [REPORT.md §9](research/REPORT.md#9-known-limits).
281
285
 
282
286
  ## Repository layout
283
287
 
284
- | | files |
288
+ | folder | what is in it |
285
289
  |---|---|
286
- | **Product** (what `pip install graph-mind-memory` installs) | `graph_mind_mcp_server.py` (MCP server), `automatic_capture*.py` (capture service), `install.py`, `brain_log.py` (sharing across PCs), `forget.py` (deleting), `local_brain.py` / `conversation_memory.py` / `coding_memory.py` / `development_memory.py` (stores), `semantic_recall.py` / `local_embedder.py` / `vector_cache.py` / `embedding_warmup.py` (search), and their helpers |
287
- | **Benchmarks** | `product_answer_eval.py`, `official_judge_v073.py`, `rival_mem0.py`, `rival_mempalace.py`, `rival_clean_prereg.py`, and the result files under `runs/` |
288
- | **Research phase** | the other modules: earlier extraction pipelines and analyses that REPORT.md cites |
289
- | **Tests** | `test_*.py` |
290
+ | [`src/`](src) | the product, exactly what `pip install graph-mind-memory` installs: `graph_mind_mcp_server.py` (MCP server), `automatic_capture*.py` (capture service), `install.py`, `brain_log.py` (sharing across PCs), `forget.py` (deleting), the stores, and search (`semantic_recall.py`, `local_embedder.py`) |
291
+ | [`tests/`](tests) | unit tests, run in CI on Windows, Linux and macOS |
292
+ | [`research/`](research) | the benchmark and research-phase scripts, [REPORT.md](research/REPORT.md), and every run's results under `runs/` |
293
+ | [`demo/`](demo) | the README's GIF and the script that records it |
290
294
 
291
295
  ## Security
292
296
 
@@ -0,0 +1,63 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/associative_memory.py
5
+ src/automatic_capture.py
6
+ src/automatic_capture_cli.py
7
+ src/brain_log.py
8
+ src/clock.py
9
+ src/coding_memory.py
10
+ src/conversation_memory.py
11
+ src/development_memory.py
12
+ src/development_paths.py
13
+ src/embedding_warmup.py
14
+ src/entity_timeline.py
15
+ src/forget.py
16
+ src/graph_mind_mcp_server.py
17
+ src/install.py
18
+ src/local_brain.py
19
+ src/local_embedder.py
20
+ src/semantic_llm_extractor.py
21
+ src/semantic_memory.py
22
+ src/semantic_recall.py
23
+ src/temporal_resolver.py
24
+ src/universal_personal_memory.py
25
+ src/vector_cache.py
26
+ src/graph_mind_memory.egg-info/PKG-INFO
27
+ src/graph_mind_memory.egg-info/SOURCES.txt
28
+ src/graph_mind_memory.egg-info/dependency_links.txt
29
+ src/graph_mind_memory.egg-info/entry_points.txt
30
+ src/graph_mind_memory.egg-info/requires.txt
31
+ src/graph_mind_memory.egg-info/top_level.txt
32
+ tests/test_associative_memory.py
33
+ tests/test_automatic_capture.py
34
+ tests/test_capture_health.py
35
+ tests/test_coding_memory.py
36
+ tests/test_cold_start.py
37
+ tests/test_concurrent_use.py
38
+ tests/test_conversation_memory.py
39
+ tests/test_executable_memory.py
40
+ tests/test_executable_plan_v2_diagnostic.py
41
+ tests/test_forget.py
42
+ tests/test_forget_conversation.py
43
+ tests/test_graph_mind_mcp_server.py
44
+ tests/test_ingestion_gate.py
45
+ tests/test_ingestion_synonyms.py
46
+ tests/test_install.py
47
+ tests/test_llm_concept_planner.py
48
+ tests/test_local_brain.py
49
+ tests/test_longmemeval_adapter.py
50
+ tests/test_longmemeval_ingestion_calibration.py
51
+ tests/test_longmemeval_ingestion_preflight.py
52
+ tests/test_longmemeval_ingestion_v7.py
53
+ tests/test_memory_algebra.py
54
+ tests/test_precondition_verifier.py
55
+ tests/test_recent_recall.py
56
+ tests/test_resume_retries.py
57
+ tests/test_secret_masking.py
58
+ tests/test_semantic_llm_extractor.py
59
+ tests/test_semantic_memory.py
60
+ tests/test_semantic_recall.py
61
+ tests/test_shared_brain.py
62
+ tests/test_temporal_state.py
63
+ tests/test_universal_personal_memory.py
@@ -23,6 +23,9 @@ HERE = Path(__file__).resolve().parent
23
23
  SERVER = HERE / "graph_mind_mcp_server.py"
24
24
  NAME = "graph-mind-memory"
25
25
  ENV = {"PYTHONUTF8": "1", "PYTHONIOENCODING": "utf-8"}
26
+ # apps whose transcripts the capture service reads: every turn is saved without the model,
27
+ # so the model need not spend a tool call on brain_remember for ordinary chat
28
+ CAPTURED = {**ENV, "GRAPH_MIND_CAPTURED": "1"}
26
29
 
27
30
 
28
31
  def step(message):
@@ -31,7 +34,7 @@ def step(message):
31
34
 
32
35
  def packages():
33
36
  done = subprocess.run([sys.executable, "-m", "pip", "install", "--disable-pip-version-check",
34
- "-q", "-r", str(HERE / "requirements.txt")])
37
+ "-q", "-r", str(HERE.parent / "requirements.txt")])
35
38
  if done.returncode:
36
39
  # PyTorch ships files nested deep enough to pass Windows' 260-character path limit when
37
40
  # Python lives under a long folder: pip then fails halfway with "No such file".
@@ -42,10 +45,10 @@ def packages():
42
45
  " or install Python or this folder under a shorter path, e.g. C:\\graph-mind")
43
46
 
44
47
 
45
- def _json_entry(path: Path, key: str = "mcpServers", **extra) -> None:
48
+ def _json_entry(path: Path, key: str = "mcpServers", env=ENV, **extra) -> None:
46
49
  settings = json.loads(path.read_text(encoding="utf-8")) if path.exists() else {}
47
50
  servers = settings.setdefault(key, {})
48
- environment = {**servers.get(NAME, {}).get("env", {}), **ENV} # keep the user's own settings
51
+ environment = {**servers.get(NAME, {}).get("env", {}), **env} # keep the user's own settings
49
52
  servers[NAME] = {**extra, "command": sys.executable, "args": [str(SERVER)], "env": environment}
50
53
  temporary = path.with_suffix(".graph-mind.tmp")
51
54
  temporary.write_text(json.dumps(settings, ensure_ascii=False, indent=2), encoding="utf-8")
@@ -55,14 +58,17 @@ def _json_entry(path: Path, key: str = "mcpServers", **extra) -> None:
55
58
  def claude_code(home: Path) -> str:
56
59
  claude = shutil.which("claude") # full path: an npm install is claude.CMD, which
57
60
  if claude: # Windows will not launch by its bare name
58
- subprocess.run([claude, "mcp", "remove", "-s", "user", NAME], capture_output=True)
59
- environment = [flag for key, value in ENV.items() for flag in ("-e", f"{key}={value}")]
60
- # the name before -e: the CLI's -e takes every value that follows, the name included
61
- subprocess.run([claude, "mcp", "add", "-s", "user", NAME, *environment, "--",
62
- sys.executable, str(SERVER)], check=True, capture_output=True)
63
- return "Claude Code"
61
+ environment = [flag for key, value in CAPTURED.items() for flag in ("-e", f"{key}={value}")]
62
+ try:
63
+ subprocess.run([claude, "mcp", "remove", "-s", "user", NAME], capture_output=True)
64
+ # the name before -e: the CLI's -e takes every value that follows, the name included
65
+ subprocess.run([claude, "mcp", "add", "-s", "user", NAME, *environment, "--",
66
+ sys.executable, str(SERVER)], check=True, capture_output=True)
67
+ return "Claude Code"
68
+ except (OSError, subprocess.CalledProcessError):
69
+ pass # Windows refused to launch it (access denied, seen live): write the file
64
70
  if (home / ".claude.json").exists():
65
- _json_entry(home / ".claude.json", type="stdio")
71
+ _json_entry(home / ".claude.json", env=CAPTURED, type="stdio")
66
72
  return "Claude Code"
67
73
  return ""
68
74
 
@@ -158,9 +164,9 @@ def codex(home: Path) -> str:
158
164
  text = config.read_text(encoding="utf-8") if config.exists() else ""
159
165
  header = f"[mcp_servers.{NAME}]"
160
166
  text = _without_codex_block(text)
161
- environment = "\n".join(f"{key} = '{value}'" for key, value in ENV.items())
162
- # required: otherwise Codex leaves out a server still starting when the first question comes
163
- text += (f"\n{header}\nrequired = true\nstartup_timeout_sec = 60\n"
167
+ environment = "\n".join(f"{key} = '{value}'" for key, value in CAPTURED.items())
168
+ # not required: a memory server that fails to start must never keep a chat from starting
169
+ text += (f"\n{header}\nstartup_timeout_sec = 60\n"
164
170
  f"command = '{sys.executable}'\nargs = ['{SERVER}']\n\n"
165
171
  f"[mcp_servers.{NAME}.env]\n{environment}\n")
166
172
  config.write_text(text, encoding="utf-8")
@@ -171,7 +177,7 @@ def capture_service(start: bool) -> str:
171
177
  sys.path.insert(0, str(HERE))
172
178
  from automatic_capture import _atomic_json, default_config_path, default_service_state_dir, make_config
173
179
  if not default_config_path().exists():
174
- config = make_config(workspace=str(HERE), project_id="project:graph-mind",
180
+ config = make_config(workspace=str(HERE.parent), project_id="project:graph-mind",
175
181
  session_id="service:automatic-capture-v0.2",
176
182
  sessions_root=str(Path.home() / ".codex" / "sessions"))
177
183
  # Conversations only. Watching a code folder is opt-in: this folder is Graph-MIND itself,
@@ -263,7 +269,7 @@ def main(argv=None):
263
269
  sys.path.insert(0, str(HERE))
264
270
  from brain_log import _decode
265
271
  _decode(args.join)
266
- if not args.skip_packages and (HERE / "requirements.txt").exists(): # pip: already done
272
+ if not args.skip_packages and (HERE.parent / "requirements.txt").exists(): # pip: already done
267
273
  step("installing packages (the first time takes a few minutes)")
268
274
  packages()
269
275
  home = Path.home()