graph-mind-memory 0.6.3__tar.gz → 0.6.4__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 (69) hide show
  1. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/PKG-INFO +2 -2
  2. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/README.md +1 -1
  3. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/pyproject.toml +1 -1
  4. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/brain_log.py +41 -9
  5. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/graph_mind_memory.egg-info/PKG-INFO +2 -2
  6. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/graph_mind_memory.egg-info/SOURCES.txt +1 -0
  7. graph_mind_memory-0.6.4/tests/test_hub_retry.py +33 -0
  8. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/LICENSE +0 -0
  9. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/setup.cfg +0 -0
  10. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/associative_memory.py +0 -0
  11. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/automatic_capture.py +0 -0
  12. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/automatic_capture_cli.py +0 -0
  13. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/brain_view.py +0 -0
  14. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/clock.py +0 -0
  15. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/coding_memory.py +0 -0
  16. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/conversation_memory.py +0 -0
  17. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/development_memory.py +0 -0
  18. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/development_paths.py +0 -0
  19. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/embedding_warmup.py +0 -0
  20. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/entity_timeline.py +0 -0
  21. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/forget.py +0 -0
  22. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/graph_mind_mcp_server.py +0 -0
  23. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/graph_mind_memory.egg-info/dependency_links.txt +0 -0
  24. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/graph_mind_memory.egg-info/entry_points.txt +0 -0
  25. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/graph_mind_memory.egg-info/requires.txt +0 -0
  26. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/graph_mind_memory.egg-info/top_level.txt +0 -0
  27. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/install.py +0 -0
  28. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/local_brain.py +0 -0
  29. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/local_embedder.py +0 -0
  30. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/semantic_llm_extractor.py +0 -0
  31. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/semantic_memory.py +0 -0
  32. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/semantic_recall.py +0 -0
  33. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/temporal_resolver.py +0 -0
  34. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/universal_personal_memory.py +0 -0
  35. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/src/vector_cache.py +0 -0
  36. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_associative_memory.py +0 -0
  37. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_automatic_capture.py +0 -0
  38. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_brain_view.py +0 -0
  39. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_capture_health.py +0 -0
  40. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_coding_memory.py +0 -0
  41. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_cold_start.py +0 -0
  42. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_concurrent_use.py +0 -0
  43. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_conversation_memory.py +0 -0
  44. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_executable_memory.py +0 -0
  45. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_executable_plan_v2_diagnostic.py +0 -0
  46. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_forget.py +0 -0
  47. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_forget_conversation.py +0 -0
  48. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_graph_mind_mcp_server.py +0 -0
  49. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_ingestion_gate.py +0 -0
  50. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_ingestion_synonyms.py +0 -0
  51. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_install.py +0 -0
  52. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_llm_concept_planner.py +0 -0
  53. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_local_brain.py +0 -0
  54. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_longmemeval_adapter.py +0 -0
  55. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_longmemeval_ingestion_calibration.py +0 -0
  56. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_longmemeval_ingestion_preflight.py +0 -0
  57. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_longmemeval_ingestion_v7.py +0 -0
  58. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_memory_algebra.py +0 -0
  59. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_piece_index.py +0 -0
  60. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_precondition_verifier.py +0 -0
  61. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_recent_recall.py +0 -0
  62. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_resume_retries.py +0 -0
  63. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_secret_masking.py +0 -0
  64. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_semantic_llm_extractor.py +0 -0
  65. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_semantic_memory.py +0 -0
  66. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_semantic_recall.py +0 -0
  67. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_shared_brain.py +0 -0
  68. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_temporal_state.py +0 -0
  69. {graph_mind_memory-0.6.3 → graph_mind_memory-0.6.4}/tests/test_universal_personal_memory.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graph-mind-memory
3
- Version: 0.6.3
3
+ Version: 0.6.4
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
@@ -247,7 +247,7 @@ tools get confused with each other. Version 0.1 had thirteen.
247
247
  ## See your memory
248
248
 
249
249
  <div align="center">
250
- <img src="demo/brain.gif" alt="Three weeks of real coding chats as a 3D graph: coloured regions of related memories, joined by links" width="760">
250
+ <img src="demo/brain.png" alt="Three weeks of real coding chats as a 3D graph: coloured regions of related memories, joined by links" width="900">
251
251
  </div>
252
252
 
253
253
  ```bash
@@ -219,7 +219,7 @@ tools get confused with each other. Version 0.1 had thirteen.
219
219
  ## See your memory
220
220
 
221
221
  <div align="center">
222
- <img src="demo/brain.gif" alt="Three weeks of real coding chats as a 3D graph: coloured regions of related memories, joined by links" width="760">
222
+ <img src="demo/brain.png" alt="Three weeks of real coding chats as a 3D graph: coloured regions of related memories, joined by links" width="900">
223
223
  </div>
224
224
 
225
225
  ```bash
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "graph-mind-memory"
7
- version = "0.6.3"
7
+ version = "0.6.4"
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 @@ import os
35
35
  import platform
36
36
  import re
37
37
  import sqlite3
38
+ import subprocess
38
39
 
39
40
  KINDS = ("memory", "turn", "forget") # forget: ids another PC erased (forget.py)
40
41
 
@@ -105,12 +106,14 @@ def local_postgres() -> str:
105
106
  with fasteners.InterProcessLock(str(data.parent / "postgres.lock")):
106
107
  fresh = not (data / "PG_VERSION").exists()
107
108
  if fresh:
108
- initdb(["--auth=trust", "--encoding=utf8", "-U", "postgres"], pgdata=data,
109
- **_DETACHED)
110
- running = PostmasterInfo.read_from_pgdata(data)
111
- if running is None or not running.is_running():
112
- pg_ctl(["-w", "-o", f"-p {PORT}", "-l", str(data / "log"), "start"],
113
- pgdata=data, timeout=60, **_DETACHED)
109
+ _initdb_with_retries(initdb, data)
110
+
111
+ def start():
112
+ running = PostmasterInfo.read_from_pgdata(data)
113
+ if running is None or not running.is_running(): # a failed try may have
114
+ pg_ctl(["-w", "-o", f"-p {PORT}", "-l", str(data / "log"), "start"],
115
+ pgdata=data, timeout=60, **_DETACHED) # started it anyway
116
+ _retry(start, attempts=10)
114
117
  if fresh:
115
118
  import psycopg
116
119
  with psycopg.connect(_admin_uri(), autocommit=True) as admin:
@@ -120,11 +123,40 @@ def local_postgres() -> str:
120
123
  return _LOCAL_URI["uri"]
121
124
 
122
125
 
126
+ def _retry(step, attempts, undo=None):
127
+ """Postgres's own tools fail at random on some Windows PCs: on one with AhnLab V3 running,
128
+ initdb succeeded in 3-6 of 10 identical runs ("popen failure: Bad file descriptor" when
129
+ its pipes to postgres broke), whichever way it was launched, and pg_ctl start failed too.
130
+ Every attempt is independent, so trying again is the fix: ten attempts at even odds fail
131
+ together 0.1% of the time."""
132
+ import time
133
+ for attempt in range(attempts):
134
+ try:
135
+ return step()
136
+ except subprocess.CalledProcessError:
137
+ if undo:
138
+ undo()
139
+ if attempt == attempts - 1:
140
+ raise
141
+ time.sleep(0.5)
142
+
143
+
144
+ def _initdb_with_retries(initdb, data):
145
+ """Create the cluster, once per PC. A failed run leaves the folder half made; clear it."""
146
+ import shutil
147
+ _retry(lambda: initdb(["--auth=trust", "--encoding=utf8", "-U", "postgres"], pgdata=data,
148
+ **_DETACHED),
149
+ attempts=10, undo=lambda: shutil.rmtree(data, ignore_errors=True))
150
+
151
+
123
152
  # Started from Claude's or Codex's process, the server shared that program's console, and closing
124
153
  # it sent Ctrl+C to Postgres too (0xC000013A in its log): the hub went down with an app window.
125
- # Its own process group, with no console, it outlives whoever happened to start it.
126
- _DETACHED = ({"creationflags": 0x00000008 | 0x00000200} # DETACHED_PROCESS | NEW_PROCESS_GROUP
127
- if os.name == "nt" else {"start_new_session": True})
154
+ # Its own process group, with no console, it outlives whoever happened to start it. Its stdin is
155
+ # the null device: Postgres must not hold on to the caller's (an MCP server's is the pipe its app
156
+ # talks through), and a caller may have none at all (pythonw).
157
+ _DETACHED = ({"creationflags": 0x00000008 | 0x00000200, # DETACHED_PROCESS | NEW_PROCESS_GROUP
158
+ "stdin": subprocess.DEVNULL}
159
+ if os.name == "nt" else {"start_new_session": True, "stdin": subprocess.DEVNULL})
128
160
 
129
161
 
130
162
  def _admin_uri() -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: graph-mind-memory
3
- Version: 0.6.3
3
+ Version: 0.6.4
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
@@ -247,7 +247,7 @@ tools get confused with each other. Version 0.1 had thirteen.
247
247
  ## See your memory
248
248
 
249
249
  <div align="center">
250
- <img src="demo/brain.gif" alt="Three weeks of real coding chats as a 3D graph: coloured regions of related memories, joined by links" width="760">
250
+ <img src="demo/brain.png" alt="Three weeks of real coding chats as a 3D graph: coloured regions of related memories, joined by links" width="900">
251
251
  </div>
252
252
 
253
253
  ```bash
@@ -43,6 +43,7 @@ tests/test_executable_plan_v2_diagnostic.py
43
43
  tests/test_forget.py
44
44
  tests/test_forget_conversation.py
45
45
  tests/test_graph_mind_mcp_server.py
46
+ tests/test_hub_retry.py
46
47
  tests/test_ingestion_gate.py
47
48
  tests/test_ingestion_synonyms.py
48
49
  tests/test_install.py
@@ -0,0 +1,33 @@
1
+ """Postgres's tools fail at random on some Windows PCs (AhnLab V3): hub setup tries again."""
2
+ import _isolate # noqa: F401 (first: never touch the real ~/.graph-mind)
3
+ import subprocess
4
+ import unittest
5
+ from unittest import mock
6
+
7
+ import brain_log
8
+
9
+
10
+ class HubRetryTests(unittest.TestCase):
11
+ def test_a_flaky_step_is_tried_again_and_cleaned_up_between(self):
12
+ calls, undone = [], []
13
+
14
+ def flaky():
15
+ calls.append(1)
16
+ if len(calls) < 3:
17
+ raise subprocess.CalledProcessError(1, "initdb")
18
+ return "ok"
19
+
20
+ with mock.patch("time.sleep"):
21
+ self.assertEqual(brain_log._retry(flaky, attempts=10, undo=lambda: undone.append(1)),
22
+ "ok")
23
+ self.assertEqual((len(calls), len(undone)), (3, 2))
24
+
25
+ def test_gives_up_after_the_last_attempt(self):
26
+ def broken():
27
+ raise subprocess.CalledProcessError(1, "pg_ctl")
28
+ with mock.patch("time.sleep"), self.assertRaises(subprocess.CalledProcessError):
29
+ brain_log._retry(broken, attempts=3)
30
+
31
+
32
+ if __name__ == "__main__":
33
+ unittest.main()