mneme-core 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. mneme_core-2.0.0/.gitignore +88 -0
  2. mneme_core-2.0.0/PKG-INFO +75 -0
  3. mneme_core-2.0.0/README.md +32 -0
  4. mneme_core-2.0.0/pyproject.toml +125 -0
  5. mneme_core-2.0.0/src/mneme_core/__init__.py +4 -0
  6. mneme_core-2.0.0/src/mneme_core/__main__.py +8 -0
  7. mneme_core-2.0.0/src/mneme_core/approval.py +183 -0
  8. mneme_core-2.0.0/src/mneme_core/audit.py +82 -0
  9. mneme_core-2.0.0/src/mneme_core/bench/__init__.py +40 -0
  10. mneme_core-2.0.0/src/mneme_core/bench/hardware.py +110 -0
  11. mneme_core-2.0.0/src/mneme_core/bench/harness.py +258 -0
  12. mneme_core-2.0.0/src/mneme_core/bench/metrics.py +142 -0
  13. mneme_core-2.0.0/src/mneme_core/bench/synth.py +207 -0
  14. mneme_core-2.0.0/src/mneme_core/capability.py +131 -0
  15. mneme_core-2.0.0/src/mneme_core/cli.py +1437 -0
  16. mneme_core-2.0.0/src/mneme_core/compression/__init__.py +106 -0
  17. mneme_core-2.0.0/src/mneme_core/compression/config.py +100 -0
  18. mneme_core-2.0.0/src/mneme_core/compression/ledger.py +447 -0
  19. mneme_core-2.0.0/src/mneme_core/compression/llm.py +155 -0
  20. mneme_core-2.0.0/src/mneme_core/compression/pipeline.py +543 -0
  21. mneme_core-2.0.0/src/mneme_core/compression/prompts/compress-en.md +108 -0
  22. mneme_core-2.0.0/src/mneme_core/compression/staging.py +363 -0
  23. mneme_core-2.0.0/src/mneme_core/connectors.py +158 -0
  24. mneme_core-2.0.0/src/mneme_core/connectors_net.py +231 -0
  25. mneme_core-2.0.0/src/mneme_core/console.py +278 -0
  26. mneme_core-2.0.0/src/mneme_core/distill/__init__.py +74 -0
  27. mneme_core-2.0.0/src/mneme_core/distill/adaptive_topk.py +65 -0
  28. mneme_core-2.0.0/src/mneme_core/distill/audit.py +248 -0
  29. mneme_core-2.0.0/src/mneme_core/distill/compressed_format.py +90 -0
  30. mneme_core-2.0.0/src/mneme_core/distill/injection_dedup.py +101 -0
  31. mneme_core-2.0.0/src/mneme_core/distill/shell_compress.py +207 -0
  32. mneme_core-2.0.0/src/mneme_core/fts5/__init__.py +5 -0
  33. mneme_core-2.0.0/src/mneme_core/fts5/indexer.py +675 -0
  34. mneme_core-2.0.0/src/mneme_core/fts5/locale/__init__.py +6 -0
  35. mneme_core-2.0.0/src/mneme_core/fts5/locale/tr.py +120 -0
  36. mneme_core-2.0.0/src/mneme_core/injection.py +76 -0
  37. mneme_core-2.0.0/src/mneme_core/kg/__init__.py +42 -0
  38. mneme_core-2.0.0/src/mneme_core/kg/client.py +97 -0
  39. mneme_core-2.0.0/src/mneme_core/kg/episode_stage.py +190 -0
  40. mneme_core-2.0.0/src/mneme_core/kg/flush.py +40 -0
  41. mneme_core-2.0.0/src/mneme_core/kg/worker.py +326 -0
  42. mneme_core-2.0.0/src/mneme_core/modes.py +375 -0
  43. mneme_core-2.0.0/src/mneme_core/modes_cli.py +151 -0
  44. mneme_core-2.0.0/src/mneme_core/patterns.py +265 -0
  45. mneme_core-2.0.0/src/mneme_core/privacy.py +90 -0
  46. mneme_core-2.0.0/src/mneme_core/py.typed +1 -0
  47. mneme_core-2.0.0/src/mneme_core/retrieval/__init__.py +54 -0
  48. mneme_core-2.0.0/src/mneme_core/retrieval/dense.py +425 -0
  49. mneme_core-2.0.0/src/mneme_core/retrieval/planner.py +73 -0
  50. mneme_core-2.0.0/src/mneme_core/retrieval/rrf.py +412 -0
  51. mneme_core-2.0.0/src/mneme_core/retrieval/telemetry.py +83 -0
  52. mneme_core-2.0.0/src/mneme_core/security.py +181 -0
  53. mneme_core-2.0.0/src/mneme_core/security_bench.py +241 -0
  54. mneme_core-2.0.0/src/mneme_core/taint.py +115 -0
  55. mneme_core-2.0.0/src/mneme_core/telemetry/__init__.py +27 -0
  56. mneme_core-2.0.0/src/mneme_core/telemetry/writer.py +237 -0
  57. mneme_core-2.0.0/src/mneme_core/temporal/__init__.py +58 -0
  58. mneme_core-2.0.0/src/mneme_core/temporal/backend.py +173 -0
  59. mneme_core-2.0.0/src/mneme_core/temporal/claim.py +237 -0
  60. mneme_core-2.0.0/src/mneme_core/temporal/extract.py +279 -0
  61. mneme_core-2.0.0/src/mneme_core/temporal/graphiti_export.py +160 -0
  62. mneme_core-2.0.0/src/mneme_core/temporal/index.py +249 -0
  63. mneme_core-2.0.0/src/mneme_core/temporal/query.py +217 -0
  64. mneme_core-2.0.0/src/mneme_core/trajectory.py +287 -0
  65. mneme_core-2.0.0/src/mneme_core/vault/__init__.py +19 -0
  66. mneme_core-2.0.0/src/mneme_core/vault/atomic_write.py +73 -0
  67. mneme_core-2.0.0/src/mneme_core/vault/config.py +177 -0
  68. mneme_core-2.0.0/src/mneme_core/vault/file_lock.py +110 -0
  69. mneme_core-2.0.0/src/mneme_core/vault/frontmatter.py +344 -0
  70. mneme_core-2.0.0/tests/__init__.py +1 -0
  71. mneme_core-2.0.0/tests/fixtures/canonical_memory_types.json +46 -0
  72. mneme_core-2.0.0/tests/fixtures/tr_locale_vectors.json +146 -0
  73. mneme_core-2.0.0/tests/integration/__init__.py +1 -0
  74. mneme_core-2.0.0/tests/integration/test_cli_doctor.py +406 -0
  75. mneme_core-2.0.0/tests/integration/test_compression_pipeline.py +280 -0
  76. mneme_core-2.0.0/tests/integration/test_concurrent_writes.py +122 -0
  77. mneme_core-2.0.0/tests/integration/test_dense_rrf_integration.py +190 -0
  78. mneme_core-2.0.0/tests/integration/test_distill_audit.py +108 -0
  79. mneme_core-2.0.0/tests/integration/test_doctor_command.py +404 -0
  80. mneme_core-2.0.0/tests/integration/test_fts5_indexer.py +1004 -0
  81. mneme_core-2.0.0/tests/integration/test_index_meta_and_prune.py +313 -0
  82. mneme_core-2.0.0/tests/integration/test_kg_episode_stage.py +154 -0
  83. mneme_core-2.0.0/tests/integration/test_kg_flush.py +67 -0
  84. mneme_core-2.0.0/tests/integration/test_kg_neo4j_integration.py +60 -0
  85. mneme_core-2.0.0/tests/integration/test_kg_worker.py +210 -0
  86. mneme_core-2.0.0/tests/integration/test_patterns.py +195 -0
  87. mneme_core-2.0.0/tests/integration/test_privacy_redaction.py +345 -0
  88. mneme_core-2.0.0/tests/integration/test_retrieval.py +393 -0
  89. mneme_core-2.0.0/tests/integration/test_staging.py +263 -0
  90. mneme_core-2.0.0/tests/integration/test_telemetry.py +240 -0
  91. mneme_core-2.0.0/tests/integration/test_temporal_datetime_roundtrip.py +390 -0
  92. mneme_core-2.0.0/tests/integration/test_temporal_integration.py +309 -0
  93. mneme_core-2.0.0/tests/integration/test_trajectory.py +119 -0
  94. mneme_core-2.0.0/tests/unit/__init__.py +1 -0
  95. mneme_core-2.0.0/tests/unit/test_approval.py +345 -0
  96. mneme_core-2.0.0/tests/unit/test_atomic_write.py +98 -0
  97. mneme_core-2.0.0/tests/unit/test_audit.py +69 -0
  98. mneme_core-2.0.0/tests/unit/test_bench_hardware.py +64 -0
  99. mneme_core-2.0.0/tests/unit/test_bench_harness.py +391 -0
  100. mneme_core-2.0.0/tests/unit/test_bench_metrics.py +120 -0
  101. mneme_core-2.0.0/tests/unit/test_bench_synth.py +82 -0
  102. mneme_core-2.0.0/tests/unit/test_canonical_memory_types_parity.py +62 -0
  103. mneme_core-2.0.0/tests/unit/test_capability.py +272 -0
  104. mneme_core-2.0.0/tests/unit/test_compression_config.py +91 -0
  105. mneme_core-2.0.0/tests/unit/test_compression_ledger.py +250 -0
  106. mneme_core-2.0.0/tests/unit/test_compression_ledger_reservation.py +200 -0
  107. mneme_core-2.0.0/tests/unit/test_compression_llm.py +166 -0
  108. mneme_core-2.0.0/tests/unit/test_connectors.py +100 -0
  109. mneme_core-2.0.0/tests/unit/test_connectors_net.py +435 -0
  110. mneme_core-2.0.0/tests/unit/test_console.py +273 -0
  111. mneme_core-2.0.0/tests/unit/test_credential_perms.py +100 -0
  112. mneme_core-2.0.0/tests/unit/test_dense.py +498 -0
  113. mneme_core-2.0.0/tests/unit/test_distill_adaptive_topk.py +64 -0
  114. mneme_core-2.0.0/tests/unit/test_distill_compressed_format.py +85 -0
  115. mneme_core-2.0.0/tests/unit/test_distill_injection_dedup.py +86 -0
  116. mneme_core-2.0.0/tests/unit/test_distill_shell_compress.py +111 -0
  117. mneme_core-2.0.0/tests/unit/test_file_lock.py +434 -0
  118. mneme_core-2.0.0/tests/unit/test_frontmatter.py +356 -0
  119. mneme_core-2.0.0/tests/unit/test_graphiti_export.py +270 -0
  120. mneme_core-2.0.0/tests/unit/test_injection.py +92 -0
  121. mneme_core-2.0.0/tests/unit/test_keypoints_extraction.py +104 -0
  122. mneme_core-2.0.0/tests/unit/test_kg_client.py +51 -0
  123. mneme_core-2.0.0/tests/unit/test_ledger_lost_write.py +232 -0
  124. mneme_core-2.0.0/tests/unit/test_migrate_schema.py +137 -0
  125. mneme_core-2.0.0/tests/unit/test_modes.py +296 -0
  126. mneme_core-2.0.0/tests/unit/test_modes_cli.py +184 -0
  127. mneme_core-2.0.0/tests/unit/test_retrieval_planner.py +126 -0
  128. mneme_core-2.0.0/tests/unit/test_retrieval_telemetry.py +265 -0
  129. mneme_core-2.0.0/tests/unit/test_retrieve_dense_seam.py +59 -0
  130. mneme_core-2.0.0/tests/unit/test_review_fixes.py +650 -0
  131. mneme_core-2.0.0/tests/unit/test_rrf_conn_leak.py +100 -0
  132. mneme_core-2.0.0/tests/unit/test_security.py +100 -0
  133. mneme_core-2.0.0/tests/unit/test_security_bench.py +246 -0
  134. mneme_core-2.0.0/tests/unit/test_staging_size_cap_perf.py +208 -0
  135. mneme_core-2.0.0/tests/unit/test_taint.py +162 -0
  136. mneme_core-2.0.0/tests/unit/test_temporal_claim.py +284 -0
  137. mneme_core-2.0.0/tests/unit/test_temporal_extract.py +346 -0
  138. mneme_core-2.0.0/tests/unit/test_temporal_query.py +245 -0
  139. mneme_core-2.0.0/tests/unit/test_tr_normalize.py +204 -0
  140. mneme_core-2.0.0/tests/unit/test_vault_config.py +109 -0
  141. mneme_core-2.0.0/tests/unit/test_vault_config_compression.py +35 -0
  142. mneme_core-2.0.0/tests/unit/test_vault_config_kg.py +45 -0
  143. mneme_core-2.0.0/tests/unit/test_vault_config_patterns_trajectory.py +29 -0
@@ -0,0 +1,88 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ share/python-wheels/
20
+ *.egg-info/
21
+ .installed.cfg
22
+ *.egg
23
+ MANIFEST
24
+ .pytest_cache/
25
+ .coverage
26
+ .coverage.*
27
+ .cache
28
+ nosetests.xml
29
+ coverage.xml
30
+ *.cover
31
+ .hypothesis/
32
+ htmlcov/
33
+ .mypy_cache/
34
+ .ruff_cache/
35
+
36
+ # Virtual envs
37
+ .env
38
+ .venv
39
+ env/
40
+ venv/
41
+ ENV/
42
+ env.bak/
43
+ venv.bak/
44
+
45
+ # Node
46
+ node_modules/
47
+ npm-debug.log*
48
+ yarn-debug.log*
49
+ yarn-error.log*
50
+ pnpm-debug.log*
51
+ .pnpm-store/
52
+ *.tsbuildinfo
53
+
54
+ # TypeScript
55
+ *.js.map
56
+ *.d.ts.map
57
+
58
+ # IDE
59
+ .vscode/
60
+ .idea/
61
+ *.swp
62
+ *.swo
63
+ .DS_Store
64
+
65
+ # OS
66
+ Thumbs.db
67
+ desktop.ini
68
+
69
+ # mneme local
70
+ .mneme/
71
+ *.local.json
72
+ vault-test/
73
+ benchmarks/results/
74
+ benchmarks/_runs/
75
+ benchmarks/*/output/
76
+ benchmarks/*/result.json
77
+ benchmarks/*/hardware.json
78
+
79
+ # Secrets
80
+ .env.local
81
+ .env.*.local
82
+ secrets/
83
+ *.key
84
+ *.pem
85
+
86
+ # Build artifacts
87
+ *.tgz
88
+ *.whl
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: mneme-core
3
+ Version: 2.0.0
4
+ Summary: Vault-native memory engine for Claude Code. FTS5 BM25, RRF-ready retrieval, gated Graphiti KG, and adaptive context tooling.
5
+ Project-URL: Homepage, https://github.com/TheGoatPsy/mneme
6
+ Project-URL: Documentation, https://github.com/TheGoatPsy/mneme/tree/main/docs
7
+ Project-URL: Repository, https://github.com/TheGoatPsy/mneme.git
8
+ Project-URL: Issues, https://github.com/TheGoatPsy/mneme/issues
9
+ Project-URL: Changelog, https://github.com/TheGoatPsy/mneme/blob/main/CHANGELOG.md
10
+ Author: Onour Impram
11
+ License: MIT
12
+ Keywords: claude-code,fts5,graphiti,knowledge-graph,markdown,mcp,memory,rrf
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Topic :: Text Processing :: Indexing
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: click<9,>=8.1
23
+ Requires-Dist: pydantic<3,>=2.7
24
+ Requires-Dist: pyyaml<7,>=6.0
25
+ Requires-Dist: structlog<25,>=24.1
26
+ Provides-Extra: compression
27
+ Requires-Dist: anthropic<1,>=0.28; extra == 'compression'
28
+ Provides-Extra: dev
29
+ Requires-Dist: mypy>=1.10; extra == 'dev'
30
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
31
+ Requires-Dist: pytest>=8.2; extra == 'dev'
32
+ Requires-Dist: ruff>=0.4.7; extra == 'dev'
33
+ Requires-Dist: testcontainers>=4.0; extra == 'dev'
34
+ Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
35
+ Provides-Extra: full
36
+ Requires-Dist: anthropic<1,>=0.28; extra == 'full'
37
+ Requires-Dist: graphiti-core<1,>=0.3; extra == 'full'
38
+ Requires-Dist: neo4j<6,>=5.21; extra == 'full'
39
+ Requires-Dist: onnxruntime<2,>=1.18; extra == 'full'
40
+ Provides-Extra: standard
41
+ Requires-Dist: onnxruntime<2,>=1.18; extra == 'standard'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # mneme-core
45
+
46
+ Python core for [mneme](https://github.com/TheGoatPsy/mneme): vault-native memory for Claude Code, also usable from Codex and any MCP client. mneme is Claude-Code-native by origin and client-neutral at the core.
47
+
48
+ This package provides:
49
+
50
+ - FTS5 BM25 full-text indexer with language-aware normalization (Turkish casefold module included as utility).
51
+ - RRF-ready retrieval pipeline at `k=60`, with FTS5 shipped and optional backends injectable by callers.
52
+ - Graphiti bi-temporal knowledge graph adapter, gated by the full profile and local Neo4j.
53
+ - Roadmap dense embedding adapter. The v1.0 standard profile reserves the runtime slot but does not ship packaged LEANN retrieval.
54
+ - Background compression pipeline with 4-D rubric (Accuracy, Depth, Context, Continuity) and cost cap ledger.
55
+ - Adaptive Context Layer: `distill.shell_compress`, `distill.injection_dedup`, `distill.adaptive_topk`, `distill.compressed_format`, and the `mneme audit` CLI.
56
+
57
+ ## Installation
58
+
59
+ ```bash
60
+ pip install mneme-core
61
+ ```
62
+
63
+ For development:
64
+
65
+ ```bash
66
+ pip install -e ".[dev]"
67
+ ```
68
+
69
+ ## Quality Gates
70
+
71
+ This package targets Python 3.11+ and passes `ruff` lint plus `mypy --strict`. Test coverage minimum is 80 percent for business logic.
72
+
73
+ ## License
74
+
75
+ MIT. See LICENSE in the repository root.
@@ -0,0 +1,32 @@
1
+ # mneme-core
2
+
3
+ Python core for [mneme](https://github.com/TheGoatPsy/mneme): vault-native memory for Claude Code, also usable from Codex and any MCP client. mneme is Claude-Code-native by origin and client-neutral at the core.
4
+
5
+ This package provides:
6
+
7
+ - FTS5 BM25 full-text indexer with language-aware normalization (Turkish casefold module included as utility).
8
+ - RRF-ready retrieval pipeline at `k=60`, with FTS5 shipped and optional backends injectable by callers.
9
+ - Graphiti bi-temporal knowledge graph adapter, gated by the full profile and local Neo4j.
10
+ - Roadmap dense embedding adapter. The v1.0 standard profile reserves the runtime slot but does not ship packaged LEANN retrieval.
11
+ - Background compression pipeline with 4-D rubric (Accuracy, Depth, Context, Continuity) and cost cap ledger.
12
+ - Adaptive Context Layer: `distill.shell_compress`, `distill.injection_dedup`, `distill.adaptive_topk`, `distill.compressed_format`, and the `mneme audit` CLI.
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pip install mneme-core
18
+ ```
19
+
20
+ For development:
21
+
22
+ ```bash
23
+ pip install -e ".[dev]"
24
+ ```
25
+
26
+ ## Quality Gates
27
+
28
+ This package targets Python 3.11+ and passes `ruff` lint plus `mypy --strict`. Test coverage minimum is 80 percent for business logic.
29
+
30
+ ## License
31
+
32
+ MIT. See LICENSE in the repository root.
@@ -0,0 +1,125 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.21"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "mneme-core"
7
+ version = "2.0.0"
8
+ description = "Vault-native memory engine for Claude Code. FTS5 BM25, RRF-ready retrieval, gated Graphiti KG, and adaptive context tooling."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Onour Impram" }]
13
+ keywords = ["claude-code", "memory", "fts5", "rrf", "graphiti", "knowledge-graph", "markdown", "mcp"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ "Topic :: Text Processing :: Indexing"
23
+ ]
24
+ dependencies = [
25
+ "pydantic>=2.7,<3",
26
+ "structlog>=24.1,<25",
27
+ "click>=8.1,<9",
28
+ "pyyaml>=6.0,<7"
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ # Standard profile: reserved runtime slot for the roadmap dense adapter.
33
+ # v1.0 ships the RRF protocol and FTS5 leg; the packaged LEANN adapter
34
+ # is intentionally not advertised as shipped.
35
+ standard = [
36
+ "onnxruntime>=1.18,<2"
37
+ ]
38
+
39
+ # Full profile: temporal knowledge graph. Adds Neo4j driver and the
40
+ # Graphiti episode/community engine. Lite/standard profiles never
41
+ # import these, and ``mneme_core.kg`` modules lazy-import graphiti_core
42
+ # so installing without this extra is a clean no-op.
43
+ full = [
44
+ "onnxruntime>=1.18,<2",
45
+ "neo4j>=5.21,<6",
46
+ "graphiti-core>=0.3,<1",
47
+ "anthropic>=0.28,<1"
48
+ ]
49
+
50
+ # Background AI compression (Phase F) lives behind an opt-in flag and
51
+ # needs an Anthropic client. The compression code path is feature-
52
+ # gated on the operator setting ``compression_enabled = true`` plus
53
+ # the API key being present in env, so the dep itself is cheap.
54
+ compression = [
55
+ "anthropic>=0.28,<1"
56
+ ]
57
+
58
+ dev = [
59
+ "pytest>=8.2",
60
+ "pytest-cov>=5.0",
61
+ "ruff>=0.4.7",
62
+ "mypy>=1.10",
63
+ "types-PyYAML>=6.0",
64
+ "testcontainers>=4.0"
65
+ ]
66
+
67
+ [project.scripts]
68
+ # Standalone core exposes its CLI as `mneme-core`. The user-facing
69
+ # `mneme` command is owned solely by mneme-cc-plugin, which re-registers
70
+ # these core command groups under it. Both packages previously declared a
71
+ # `mneme` script, so a co-install resolved to whichever pip wrote last.
72
+ mneme-core = "mneme_core.cli:main"
73
+ mneme-audit = "mneme_core.distill.audit:main"
74
+ mneme-modes = "mneme_core.modes_cli:main"
75
+ mneme-console = "mneme_core.console:main"
76
+
77
+ [project.urls]
78
+ Homepage = "https://github.com/TheGoatPsy/mneme"
79
+ Documentation = "https://github.com/TheGoatPsy/mneme/tree/main/docs"
80
+ Repository = "https://github.com/TheGoatPsy/mneme.git"
81
+ Issues = "https://github.com/TheGoatPsy/mneme/issues"
82
+ Changelog = "https://github.com/TheGoatPsy/mneme/blob/main/CHANGELOG.md"
83
+
84
+ [tool.hatch.build.targets.wheel]
85
+ packages = ["src/mneme_core"]
86
+
87
+ [tool.hatch.build.targets.wheel.force-include]
88
+ "src/mneme_core/compression/prompts/compress-en.md" = "mneme_core/compression/prompts/compress-en.md"
89
+
90
+ [tool.ruff]
91
+ line-length = 100
92
+ target-version = "py311"
93
+
94
+ [tool.ruff.lint]
95
+ select = ["E", "F", "W", "I", "B", "UP", "C4", "SIM"]
96
+
97
+ [tool.mypy]
98
+ strict = true
99
+ python_version = "3.11"
100
+ files = ["src/mneme_core"]
101
+
102
+ [[tool.mypy.overrides]]
103
+ # Optional-profile dependencies. lite/standard installs (and the CI
104
+ # type-check env, which installs only [dev]) do not have these; the code
105
+ # lazy-imports them behind runtime feature gates, so missing stubs here
106
+ # are expected rather than a type error.
107
+ module = ["graphiti_core.*", "neo4j.*", "anthropic.*", "onnxruntime.*"]
108
+ ignore_missing_imports = true
109
+
110
+ [tool.pytest.ini_options]
111
+ testpaths = ["tests"]
112
+ python_files = "test_*.py"
113
+ addopts = "-v --strict-markers --cov=mneme_core --cov-report=term-missing --cov-fail-under=80"
114
+
115
+ [tool.coverage.run]
116
+ branch = true
117
+ source = ["mneme_core"]
118
+
119
+ [tool.coverage.report]
120
+ exclude_lines = [
121
+ "pragma: no cover",
122
+ "raise NotImplementedError",
123
+ "if __name__ == .__main__.:",
124
+ "if TYPE_CHECKING:",
125
+ ]
@@ -0,0 +1,4 @@
1
+ """mneme-core: vault-native memory engine for Claude Code."""
2
+
3
+ __version__ = "2.0.0"
4
+ __all__ = ["__version__"]
@@ -0,0 +1,8 @@
1
+ """Module execution entry point for ``python -m mneme_core``."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from .cli import main
6
+
7
+ if __name__ == "__main__":
8
+ main()
@@ -0,0 +1,183 @@
1
+ """Human-approval gate for memory edits (conflict-resolution #4).
2
+
3
+ An agent *proposes* a memory edit; the proposal starts in ``PENDING`` status.
4
+ Edits in *durable* categories (IDENTITY, PREFERENCE, CLINICAL, LEGAL,
5
+ FINANCIAL) must receive explicit human approval before they may be applied.
6
+ Edits in the EPHEMERAL category (session notes, observations) may be applied
7
+ while still PENDING. A REJECTED proposal can never be applied.
8
+
9
+ All user-supplied content is passed through :func:`mneme_core.privacy.redact`
10
+ before being stored in the proposal. Proposal IDs are deterministic
11
+ (``uuid.uuid5``) so re-proposing identical inputs yields the same ID.
12
+
13
+ Pure, deterministic, no IO, no network, no clock.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import dataclasses
19
+ import uuid
20
+ from dataclasses import dataclass
21
+ from enum import Enum
22
+
23
+ from .privacy import redact
24
+
25
+ # ---------------------------------------------------------------------------
26
+ # Enumerations
27
+ # ---------------------------------------------------------------------------
28
+
29
+ _NAMESPACE = uuid.UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8") # NAMESPACE_URL
30
+
31
+
32
+ class ProposalStatus(str, Enum): # noqa: UP042
33
+ """Lifecycle status of a :class:`MemoryProposal`.
34
+
35
+ Inherits ``str`` so instances serialise directly as JSON strings and
36
+ compare equal to their string values without an extra ``.value`` call.
37
+ ``UP042`` suppresses the Ruff suggestion to use ``StrEnum`` (Python 3.11+)
38
+ to stay consistent with the ``str`` + ``Enum`` pattern used elsewhere.
39
+ """
40
+
41
+ PENDING = "PENDING"
42
+ APPROVED = "APPROVED"
43
+ REJECTED = "REJECTED"
44
+
45
+
46
+ class EditCategory(str, Enum): # noqa: UP042
47
+ """Semantic category of the memory edit being proposed.
48
+
49
+ * **EPHEMERAL** — low-stakes session/topic/observation; may be applied
50
+ without explicit approval.
51
+ * All other categories are *durable* and require human approval.
52
+ """
53
+
54
+ EPHEMERAL = "EPHEMERAL"
55
+ IDENTITY = "IDENTITY"
56
+ PREFERENCE = "PREFERENCE"
57
+ CLINICAL = "CLINICAL"
58
+ LEGAL = "LEGAL"
59
+ FINANCIAL = "FINANCIAL"
60
+
61
+
62
+ #: Categories that require explicit human approval before an edit may be applied.
63
+ DURABLE_CATEGORIES: frozenset[EditCategory] = frozenset(
64
+ {
65
+ EditCategory.IDENTITY,
66
+ EditCategory.PREFERENCE,
67
+ EditCategory.CLINICAL,
68
+ EditCategory.LEGAL,
69
+ EditCategory.FINANCIAL,
70
+ }
71
+ )
72
+
73
+ # ---------------------------------------------------------------------------
74
+ # Proposal dataclass
75
+ # ---------------------------------------------------------------------------
76
+
77
+
78
+ @dataclass(frozen=True)
79
+ class MemoryProposal:
80
+ """An immutable record of a proposed memory edit.
81
+
82
+ Attributes
83
+ ----------
84
+ proposal_id:
85
+ Deterministic ``uuid5`` derived from ``action``, ``target_path``, and
86
+ the *redacted* content. Identical inputs always produce the same ID.
87
+ action:
88
+ ``"create"`` | ``"update"`` | ``"delete"``.
89
+ target_path:
90
+ Vault-relative path of the note being created/modified/deleted.
91
+ content:
92
+ Proposed note content **after** redaction via
93
+ :func:`mneme_core.privacy.redact`.
94
+ category:
95
+ Semantic category governing approval requirements.
96
+ status:
97
+ Current lifecycle status (PENDING / APPROVED / REJECTED).
98
+ trust:
99
+ Proposer identity string; defaults to ``"agent"``.
100
+ """
101
+
102
+ proposal_id: str
103
+ action: str
104
+ target_path: str
105
+ content: str
106
+ category: EditCategory
107
+ status: ProposalStatus
108
+ trust: str
109
+
110
+
111
+ # ---------------------------------------------------------------------------
112
+ # Pure functions
113
+ # ---------------------------------------------------------------------------
114
+
115
+
116
+ def requires_human_approval(category: EditCategory) -> bool:
117
+ """Return ``True`` iff *category* is in :data:`DURABLE_CATEGORIES`."""
118
+ return category in DURABLE_CATEGORIES
119
+
120
+
121
+ def propose(
122
+ *,
123
+ action: str,
124
+ target_path: str,
125
+ content: str,
126
+ category: EditCategory,
127
+ trust: str = "agent",
128
+ ) -> MemoryProposal:
129
+ """Create a new :class:`MemoryProposal` in PENDING status.
130
+
131
+ Content is redacted via :func:`~mneme_core.privacy.redact` before being
132
+ stored. The ``proposal_id`` is a deterministic ``uuid5`` derived from
133
+ ``action + NUL + target_path + NUL + redacted_content`` so that identical
134
+ inputs always yield the same proposal ID.
135
+ """
136
+ redacted = redact(content)
137
+ # category and trust are part of proposal identity: an EPHEMERAL and a
138
+ # CLINICAL proposal for the same path+content must NOT share a proposal_id,
139
+ # else a downstream store keyed on it could alias the durable edit to an
140
+ # already-applied ephemeral one and bypass the human-approval gate.
141
+ seed = f"{action}\x00{target_path}\x00{category.value}\x00{trust}\x00{redacted}"
142
+ proposal_id = str(uuid.uuid5(_NAMESPACE, seed))
143
+ return MemoryProposal(
144
+ proposal_id=proposal_id,
145
+ action=action,
146
+ target_path=target_path,
147
+ content=redacted,
148
+ category=category,
149
+ status=ProposalStatus.PENDING,
150
+ trust=trust,
151
+ )
152
+
153
+
154
+ def approve(proposal: MemoryProposal) -> MemoryProposal:
155
+ """Return a copy of *proposal* with ``status`` set to APPROVED.
156
+
157
+ Idempotent: approving an already-approved proposal returns an equivalent
158
+ object unchanged.
159
+ """
160
+ return dataclasses.replace(proposal, status=ProposalStatus.APPROVED)
161
+
162
+
163
+ def reject(proposal: MemoryProposal) -> MemoryProposal:
164
+ """Return a copy of *proposal* with ``status`` set to REJECTED."""
165
+ return dataclasses.replace(proposal, status=ProposalStatus.REJECTED)
166
+
167
+
168
+ def can_apply(proposal: MemoryProposal) -> bool:
169
+ """Return ``True`` iff the proposal may be applied to the vault.
170
+
171
+ Rules (deterministic, pure):
172
+
173
+ * A REJECTED proposal can **never** be applied.
174
+ * An APPROVED proposal can always be applied.
175
+ * A PENDING proposal may be applied only when its category is EPHEMERAL
176
+ (i.e. ``requires_human_approval`` is ``False``).
177
+ """
178
+ if proposal.status == ProposalStatus.REJECTED:
179
+ return False
180
+ if proposal.status == ProposalStatus.APPROVED:
181
+ return True
182
+ # PENDING: allowed only for non-durable (ephemeral) categories.
183
+ return not requires_human_approval(proposal.category)
@@ -0,0 +1,82 @@
1
+ """Read-only vault audit aggregator (the console surface, v1).
2
+
3
+ Produces a deterministic, read-only snapshot of the vault: note counts by
4
+ memory type plus a security-scan summary. This is the programmatic/text console
5
+ surface; a browser audit UI (timeline, graph explorer, token-ROI dashboard) is
6
+ deferred. The aggregator never writes and never raises on a single bad file.
7
+
8
+ No LLM, no network.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from dataclasses import dataclass
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ from .security import scan_vault, summarize
18
+ from .vault.frontmatter import parse
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class AuditReport:
23
+ """A read-only vault health + safety snapshot."""
24
+
25
+ note_count: int
26
+ type_counts: dict[str, int]
27
+ security: dict[str, Any]
28
+
29
+
30
+ def build_audit(vault_root: Path) -> AuditReport:
31
+ """Walk the vault markdown and build a read-only audit snapshot.
32
+
33
+ Counts notes by frontmatter ``type`` (notes without parseable frontmatter
34
+ are bucketed as ``"(none)"``) and folds in a security-scan summary. Symlinks
35
+ that escape the vault are skipped; unreadable or malformed files never crash
36
+ the audit.
37
+ """
38
+ root_resolved = vault_root.resolve()
39
+ type_counts: dict[str, int] = {}
40
+ note_count = 0
41
+ for md_path in sorted(vault_root.rglob("*.md")):
42
+ try:
43
+ resolved = md_path.resolve()
44
+ except OSError:
45
+ continue
46
+ if not resolved.is_relative_to(root_resolved):
47
+ continue
48
+ try:
49
+ text = md_path.read_text(encoding="utf-8", errors="replace")
50
+ except OSError:
51
+ continue
52
+ note_count += 1
53
+ type_name = "(none)"
54
+ try:
55
+ front, _ = parse(text)
56
+ if front is not None and front.type:
57
+ type_name = front.type
58
+ except Exception: # noqa: BLE001 - audit must never crash on a bad note
59
+ type_name = "(unparseable)"
60
+ type_counts[type_name] = type_counts.get(type_name, 0) + 1
61
+
62
+ security = summarize(scan_vault(vault_root))
63
+ return AuditReport(note_count=note_count, type_counts=type_counts, security=security)
64
+
65
+
66
+ def render_audit(report: AuditReport) -> str:
67
+ """Render an :class:`AuditReport` as a plain-text report."""
68
+ lines = ["# Vault audit", "", f"Notes: {report.note_count}", "", "## By type"]
69
+ if report.type_counts:
70
+ for type_name, count in sorted(report.type_counts.items()):
71
+ lines.append(f"- {type_name}: {count}")
72
+ else:
73
+ lines.append("- (no notes)")
74
+ lines.extend(["", "## Security"])
75
+ sec = report.security
76
+ lines.append(f"- files flagged: {sec.get('files_flagged', 0)}")
77
+ lines.append(f"- total findings: {sec.get('total_findings', 0)}")
78
+ by_kind = sec.get("by_kind", {})
79
+ if isinstance(by_kind, dict):
80
+ for kind, count in sorted(by_kind.items()):
81
+ lines.append(f" - {kind}: {count}")
82
+ return "\n".join(lines) + "\n"
@@ -0,0 +1,40 @@
1
+ """Benchmark helpers shared by the ``benchmarks/`` runner scripts.
2
+
3
+ Three focused submodules:
4
+
5
+ * :mod:`mneme_core.bench.metrics` - information-retrieval metric primitives
6
+ (nDCG@k, Recall@k, MRR) plus quantile helpers for latency distributions.
7
+ * :mod:`mneme_core.bench.synth` - deterministic synthetic corpus and query
8
+ generator. Tests and benchmarks both lean on this so they exercise the
9
+ same shape of data.
10
+ * :mod:`mneme_core.bench.hardware` - hardware/runtime probe that emits
11
+ ``hardware.json`` next to every benchmark result for reproducibility.
12
+
13
+ The submodules are intentionally small and side-effect-free. Bench scripts
14
+ own their own argparse, output, and CI guards.
15
+ """
16
+
17
+ from .hardware import HardwareSnapshot, capture_hardware
18
+ from .metrics import (
19
+ mean_reciprocal_rank,
20
+ ndcg_at_k,
21
+ percentiles,
22
+ recall_at_k,
23
+ )
24
+ from .synth import (
25
+ SyntheticCorpus,
26
+ SyntheticQuery,
27
+ build_synthetic_corpus,
28
+ )
29
+
30
+ __all__ = [
31
+ "HardwareSnapshot",
32
+ "SyntheticCorpus",
33
+ "SyntheticQuery",
34
+ "build_synthetic_corpus",
35
+ "capture_hardware",
36
+ "mean_reciprocal_rank",
37
+ "ndcg_at_k",
38
+ "percentiles",
39
+ "recall_at_k",
40
+ ]