cortexm 0.3.0__tar.gz → 0.5.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 (166) hide show
  1. {cortexm-0.3.0/cortexm.egg-info → cortexm-0.5.0}/PKG-INFO +42 -20
  2. {cortexm-0.3.0 → cortexm-0.5.0}/README.md +41 -19
  3. cortexm-0.5.0/cortexm/__init__.py +151 -0
  4. cortexm-0.5.0/cortexm/api/long_recall.py +252 -0
  5. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/api/memory.py +333 -5
  6. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/decoders.py +57 -6
  7. cortexm-0.5.0/cortexm/bridge/fusion.py +245 -0
  8. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/reader.py +332 -0
  9. cortexm-0.5.0/cortexm/cli.py +650 -0
  10. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/config.py +85 -35
  11. cortexm-0.5.0/cortexm/creator.py +295 -0
  12. cortexm-0.5.0/cortexm/kernel.py +227 -0
  13. cortexm-0.5.0/cortexm/markdown_io.py +310 -0
  14. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/mcp/server.py +244 -0
  15. cortexm-0.5.0/cortexm/pipeline.py +326 -0
  16. {cortexm-0.3.0/cortexm/security → cortexm-0.5.0/cortexm/plugins}/__init__.py +0 -0
  17. cortexm-0.5.0/cortexm/plugins/security.py +208 -0
  18. cortexm-0.5.0/cortexm/plugins/structured.py +219 -0
  19. cortexm-0.5.0/cortexm/plugins/verbatim.py +355 -0
  20. cortexm-0.5.0/cortexm/router.py +205 -0
  21. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/store.py +45 -0
  22. cortexm-0.5.0/cortexm/trajectory_view.py +271 -0
  23. cortexm-0.5.0/cortexm/vsa/__init__.py +0 -0
  24. {cortexm-0.3.0 → cortexm-0.5.0/cortexm.egg-info}/PKG-INFO +42 -20
  25. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm.egg-info/SOURCES.txt +18 -0
  26. {cortexm-0.3.0 → cortexm-0.5.0}/pyproject.toml +1 -1
  27. cortexm-0.5.0/tests/test_bm25_chunk_recall_and_inspect_cli.py +255 -0
  28. cortexm-0.5.0/tests/test_fusion_security.py +205 -0
  29. cortexm-0.5.0/tests/test_kernel.py +287 -0
  30. cortexm-0.5.0/tests/test_reddit_steals_round3.py +448 -0
  31. cortexm-0.5.0/tests/test_tier443_abstention_fix.py +282 -0
  32. cortexm-0.5.0/tests/test_verbatim.py +236 -0
  33. cortexm-0.3.0/cortexm/__init__.py +0 -45
  34. cortexm-0.3.0/cortexm/cli.py +0 -295
  35. {cortexm-0.3.0 → cortexm-0.5.0}/LICENSE +0 -0
  36. {cortexm-0.3.0 → cortexm-0.5.0}/context_m.py +0 -0
  37. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/accel.py +0 -0
  38. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/api/__init__.py +0 -0
  39. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/api/chaos.py +0 -0
  40. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bench/__init__.py +0 -0
  41. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bench/abilities.py +0 -0
  42. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bench/baselines.py +0 -0
  43. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bench/beam_loader.py +0 -0
  44. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bench/generator.py +0 -0
  45. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bench/harness.py +0 -0
  46. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bench/messy.py +0 -0
  47. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bench/micro.py +0 -0
  48. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bench/ood.py +0 -0
  49. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bench/run.py +0 -0
  50. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/__init__.py +0 -0
  51. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/dates.py +0 -0
  52. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/enrich.py +0 -0
  53. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/extractor.py +0 -0
  54. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/fallback.py +0 -0
  55. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/onnx_runtime.py +0 -0
  56. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/patterns.py +0 -0
  57. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/ppr.py +0 -0
  58. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/prefilter.py +0 -0
  59. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/query_extract.py +0 -0
  60. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/rerank.py +0 -0
  61. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/bridge/writer.py +0 -0
  62. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/cognition/__init__.py +0 -0
  63. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/cognition/abstraction.py +0 -0
  64. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/cognition/analogy.py +0 -0
  65. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/cognition/engine.py +0 -0
  66. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/cognition/gaps.py +0 -0
  67. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/cognition/scanner.py +0 -0
  68. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/cortexm.py +0 -0
  69. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/enterprise/__init__.py +0 -0
  70. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/enterprise/audit.py +0 -0
  71. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/enterprise/governance.py +0 -0
  72. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/errors.py +0 -0
  73. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/features/__init__.py +0 -0
  74. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/features/git.py +0 -0
  75. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/features/prefetch.py +0 -0
  76. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/features/zk.py +0 -0
  77. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/federation/__init__.py +0 -0
  78. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/federation/crdt.py +0 -0
  79. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/federation/fabric.py +0 -0
  80. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/federation/hlc.py +0 -0
  81. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/federation/node.py +0 -0
  82. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/federation/schema_report.py +0 -0
  83. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/federation/transport.py +0 -0
  84. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/index/__init__.py +0 -0
  85. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/index/nsg.py +0 -0
  86. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/mcp/__init__.py +0 -0
  87. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/metrics.py +0 -0
  88. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/migrate/__init__.py +0 -0
  89. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/migrate/importers.py +0 -0
  90. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/provenance/__init__.py +0 -0
  91. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/provenance/agent.py +0 -0
  92. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/provenance/cose.py +0 -0
  93. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/provenance/scitt.py +0 -0
  94. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/provenance/vc.py +0 -0
  95. {cortexm-0.3.0/cortexm/server → cortexm-0.5.0/cortexm/security}/__init__.py +0 -0
  96. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/security/crypto.py +0 -0
  97. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/security/hashes.py +0 -0
  98. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/security/injection.py +0 -0
  99. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/security/mind.py +0 -0
  100. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/security/pii.py +0 -0
  101. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/security/rbac.py +0 -0
  102. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/security/sandbox.py +0 -0
  103. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/security/zk_hamming.py +0 -0
  104. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/security/zk_sql.py +0 -0
  105. {cortexm-0.3.0/cortexm/text → cortexm-0.5.0/cortexm/server}/__init__.py +0 -0
  106. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/server/metrics.py +0 -0
  107. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/server/rest.py +0 -0
  108. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/server/sparql.py +0 -0
  109. {cortexm-0.3.0/cortexm/trace → cortexm-0.5.0/cortexm/text}/__init__.py +0 -0
  110. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/text/dissim.py +0 -0
  111. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/text/embedder.py +0 -0
  112. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/text/fuzzy.py +0 -0
  113. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/text/idiolect.py +0 -0
  114. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/text/labse.py +0 -0
  115. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/text/tokenizer.py +0 -0
  116. {cortexm-0.3.0/cortexm/vsa → cortexm-0.5.0/cortexm/trace}/__init__.py +0 -0
  117. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/blob_arena.py +0 -0
  118. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/consolidate.py +0 -0
  119. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/contradictions.py +0 -0
  120. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/dedup.py +0 -0
  121. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/edges.py +0 -0
  122. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/fact.py +0 -0
  123. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/fade.py +0 -0
  124. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/lifecycle.py +0 -0
  125. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/rebuild.py +0 -0
  126. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/rules.py +0 -0
  127. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/structural.py +0 -0
  128. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/trace/tmt.py +0 -0
  129. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/util.py +0 -0
  130. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/vsa/attribution.py +0 -0
  131. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/vsa/cleanup.py +0 -0
  132. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/vsa/codecs.py +0 -0
  133. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/vsa/hologram_overlay.py +0 -0
  134. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/vsa/index.py +0 -0
  135. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/vsa/ops.py +0 -0
  136. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/vsa/palace.py +0 -0
  137. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/vsa/role_vectors.py +0 -0
  138. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/vsa/slb.py +0 -0
  139. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/vsa/tlsh_trie.py +0 -0
  140. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm/vsa/working_memory.py +0 -0
  141. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm.egg-info/dependency_links.txt +0 -0
  142. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm.egg-info/entry_points.txt +0 -0
  143. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm.egg-info/requires.txt +0 -0
  144. {cortexm-0.3.0 → cortexm-0.5.0}/cortexm.egg-info/top_level.txt +0 -0
  145. {cortexm-0.3.0 → cortexm-0.5.0}/setup.cfg +0 -0
  146. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_arxiv_improvements.py +0 -0
  147. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_cognition_and_provenance.py +0 -0
  148. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_engineering_push_2026_08_28.py +0 -0
  149. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_enterprise.py +0 -0
  150. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_fabric.py +0 -0
  151. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_federation.py +0 -0
  152. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_kinship_extraction.py +0 -0
  153. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_labse.py +0 -0
  154. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_list_superseded_intent.py +0 -0
  155. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_migration.py +0 -0
  156. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_new_modules.py +0 -0
  157. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_nsg.py +0 -0
  158. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_ppr.py +0 -0
  159. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_rerank.py +0 -0
  160. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_research_steals.py +0 -0
  161. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_research_steals_round2.py +0 -0
  162. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_rust_accel.py +0 -0
  163. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_sandbox_enrich.py +0 -0
  164. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_sparql_rest_v2.py +0 -0
  165. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_wal_recovery.py +0 -0
  166. {cortexm-0.3.0 → cortexm-0.5.0}/tests/test_zk_sql.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cortexm
3
- Version: 0.3.0
3
+ Version: 0.5.0
4
4
  Summary: Deterministic agent memory. 96 bytes per fact. Zero LLM at ingest.
5
5
  Author: Context-M Contributors
6
6
  License: Apache-2.0
@@ -27,9 +27,13 @@ Dynamic: license-file
27
27
  <a href="https://github.com/ssmurfgg04-gif/context-m/actions/workflows/test.yml"><img src="https://github.com/ssmurfgg04-gif/context-m/actions/workflows/test.yml/badge.svg?branch=main" alt="Tests"></a>
28
28
  <a href="https://github.com/ssmurfgg04-gif/context-m/actions/workflows/pr-gate.yml"><img src="https://img.shields.io/github/checks-status/ssmurfgg04-gif/context-m/main/.github/workflows/pr-gate.yml?label=pr-gate" alt="PR Gate"></a>
29
29
  <a href="https://github.com/ssmurfgg04-gif/context-m/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License"></a>
30
- <a href="https://pypi.org/project/context-m-langchain/"><img src="https://img.shields.io/pypi/v/context-m-langchain?color=%2334D058&label=pypi%20package" alt="PyPI version"></a>
31
- <a href="https://pypi.org/project/context-m-langchain/"><img src="https://img.shields.io/pypi/pyversions/context-m-langchain.svg?color=%2334D058" alt="Python versions"></a>
30
+ <a href="https://pypi.org/project/cortexm/"><img src="https://img.shields.io/pypi/v/cortexm?color=%2334D058&label=pypi%20%7Ccortexm" alt="PyPI: cortexm"></a>
31
+ <a href="https://pypi.org/project/context-m-langchain/"><img src="https://img.shields.io/pypi/v/context-m-langchain?color=%2334D058&label=pypi%20%7Clangchain" alt="PyPI: context-m-langchain"></a>
32
+ <a href="https://www.npmjs.com/package/dsh-cortexm"><img src="https://img.shields.io/npm/v/dsh-cortexm?color=%2334D058&label=npm%20%7Cdsh-cortexm" alt="npm: dsh-cortexm"></a>
33
+ <a href="https://pypi.org/project/cortexm/"><img src="https://img.shields.io/pypi/pyversions/cortexm.svg?color=%2334D058" alt="Python versions"></a>
32
34
  <a href="https://github.com/ssmurfgg04-gif/context-m/blob/main/AGENTS.md"><img src="https://img.shields.io/badge/AGENTS.md-2026-2f2f2f?logo=github" alt="AGENTS.md"></a>
35
+ <!-- MCP Registry badge — uncomment after submitting deploy/mcp-registry-submission.json to https://registry.modelcontextprotocol.io -->
36
+ <!-- <a href="https://registry.modelcontextprotocol.io/servers/contextm"><img src="https://img.shields.io/badge/MCP%20Registry-contextm-7c3aed" alt="MCP Registry"></a> -->
33
37
  <!-- Trendshift badge slot — auto-renders when the repo actually trends. -->
34
38
  <!-- <a href="https://trendshift.io/repositories/ssmurfgg04-gif/context-m"><img src="https://trendshift.io/api/badge/repositories/ssmurfgg04-gif/context-m.svg" alt="Trendshift"></a> -->
35
39
  </div>
@@ -96,23 +100,41 @@ handling accented characters without crashing the trigger.
96
100
 
97
101
  ### Tier 4.3 — LongMemEval independent judge
98
102
 
99
- | subtask | pre-fix | post-fix (2026-08-28) | Δ |
100
- |---|---|---|---|
101
- | single_hop | 1.0 | 1.0 | flat |
102
- | knowledge_update | 0.333 | **0.667** | 2× |
103
- | multi_session | 0.5 | 0.5 | flat |
104
- | temporal_reasoning | 0.5 | 0.5 | flat |
105
- | **overall** | 0.600 | **0.700** | +10pp |
106
-
107
- Three fixes drove the lift: (1) `works_at` regex contraction fix
108
- ("I'm now working at OpenAI" now extracts), (2) role pattern `|$`
109
- lookahead + uppercase support ("I'm an ML engineer" now extracts),
110
- (3) employment-anchored temporal window (resolves "where did X live
111
- when at Y" via the works_at fact's valid_from/valid_to).
112
-
113
- Reproduce: `python benchmarks/run_ood_pipeline.py --skip-render
114
- --no-enrich --no-judge --personas 4` ·
115
- `python scripts/longmemeval_judge.py`.
103
+ | subtask | pre-fix | post-fix (2026-08-28) | plugin-kernel (2026-08-29, v0.5.0) | Δ vs pre-fix |
104
+ |---|---|---|---|---|
105
+ | single_hop | 1.0 | 1.0 | 1.0 | flat |
106
+ | knowledge_update | 0.333 | 0.667 | **1.000** | 3× |
107
+ | multi_session | 0.5 | 0.5 | 0.5 | flat |
108
+ | temporal_reasoning | 0.5 | 0.5 | 0.5 | flat |
109
+ | **overall** | 0.600 | 0.700 | **0.800** | +20pp |
110
+
111
+ The v0.5.0 lift (0.700 → 0.800) comes from the new plugin kernel
112
+ + verbatim tier: when the structured extractor misses a fact
113
+ ("I'm now working at OpenAI" → role pattern), the FTS5 + int8
114
+ dense path catches it verbatim. Fusion then merges both tiers
115
+ at μ=0 cost. The 2 misses that remain are aggregation phrasing
116
+ ("List all the places Bob has worked") and yes/no answer shape
117
+ ("Did Bob move between sessions") — extractor limitations, not
118
+ memory limitations.
119
+
120
+ Reproduce: `python scripts/longmemeval_judge.py --out
121
+ benchmarks/results/longmemeval_v0.5.0.json` ·
122
+ [`benchmarks/results/longmemeval_v0.5.0.json`](benchmarks/results/longmemeval_v0.5.0.json).
123
+
124
+ Pre-plugin-kernel fixes (0.600 → 0.700): (1) `works_at` regex
125
+ contraction fix ("I'm now working at OpenAI" now extracts),
126
+ (2) role pattern `|$` lookahead + uppercase support ("I'm an ML
127
+ engineer" now extracts), (3) employment-anchored temporal window
128
+ (resolves "where did X live when at Y" via the works_at fact's
129
+ valid_from/valid_to).
130
+
131
+ Plugin-kernel fixes (0.700 → 0.800): the new verbatim tier (FTS5
132
+ + int8 dense, MemPalace-style) catches "I'm now working at OpenAI"
133
+ verbatim when the structured extractor's role pattern still misses
134
+ it. The fusion bridge then merges both tiers at μ=0 cost. The 2
135
+ remaining misses are not memory failures — they are answer-shape
136
+ mismatches (the judge asks for a yes/no, the context block returns
137
+ a list of facts the LLM must reason over).
116
138
 
117
139
  That is the capability profile of the μ=0 extractor on real phrasing:
118
140
  strong on change-of-state statements, weak on identity/preference
@@ -7,9 +7,13 @@
7
7
  <a href="https://github.com/ssmurfgg04-gif/context-m/actions/workflows/test.yml"><img src="https://github.com/ssmurfgg04-gif/context-m/actions/workflows/test.yml/badge.svg?branch=main" alt="Tests"></a>
8
8
  <a href="https://github.com/ssmurfgg04-gif/context-m/actions/workflows/pr-gate.yml"><img src="https://img.shields.io/github/checks-status/ssmurfgg04-gif/context-m/main/.github/workflows/pr-gate.yml?label=pr-gate" alt="PR Gate"></a>
9
9
  <a href="https://github.com/ssmurfgg04-gif/context-m/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License"></a>
10
- <a href="https://pypi.org/project/context-m-langchain/"><img src="https://img.shields.io/pypi/v/context-m-langchain?color=%2334D058&label=pypi%20package" alt="PyPI version"></a>
11
- <a href="https://pypi.org/project/context-m-langchain/"><img src="https://img.shields.io/pypi/pyversions/context-m-langchain.svg?color=%2334D058" alt="Python versions"></a>
10
+ <a href="https://pypi.org/project/cortexm/"><img src="https://img.shields.io/pypi/v/cortexm?color=%2334D058&label=pypi%20%7Ccortexm" alt="PyPI: cortexm"></a>
11
+ <a href="https://pypi.org/project/context-m-langchain/"><img src="https://img.shields.io/pypi/v/context-m-langchain?color=%2334D058&label=pypi%20%7Clangchain" alt="PyPI: context-m-langchain"></a>
12
+ <a href="https://www.npmjs.com/package/dsh-cortexm"><img src="https://img.shields.io/npm/v/dsh-cortexm?color=%2334D058&label=npm%20%7Cdsh-cortexm" alt="npm: dsh-cortexm"></a>
13
+ <a href="https://pypi.org/project/cortexm/"><img src="https://img.shields.io/pypi/pyversions/cortexm.svg?color=%2334D058" alt="Python versions"></a>
12
14
  <a href="https://github.com/ssmurfgg04-gif/context-m/blob/main/AGENTS.md"><img src="https://img.shields.io/badge/AGENTS.md-2026-2f2f2f?logo=github" alt="AGENTS.md"></a>
15
+ <!-- MCP Registry badge — uncomment after submitting deploy/mcp-registry-submission.json to https://registry.modelcontextprotocol.io -->
16
+ <!-- <a href="https://registry.modelcontextprotocol.io/servers/contextm"><img src="https://img.shields.io/badge/MCP%20Registry-contextm-7c3aed" alt="MCP Registry"></a> -->
13
17
  <!-- Trendshift badge slot — auto-renders when the repo actually trends. -->
14
18
  <!-- <a href="https://trendshift.io/repositories/ssmurfgg04-gif/context-m"><img src="https://trendshift.io/api/badge/repositories/ssmurfgg04-gif/context-m.svg" alt="Trendshift"></a> -->
15
19
  </div>
@@ -76,23 +80,41 @@ handling accented characters without crashing the trigger.
76
80
 
77
81
  ### Tier 4.3 — LongMemEval independent judge
78
82
 
79
- | subtask | pre-fix | post-fix (2026-08-28) | Δ |
80
- |---|---|---|---|
81
- | single_hop | 1.0 | 1.0 | flat |
82
- | knowledge_update | 0.333 | **0.667** | 2× |
83
- | multi_session | 0.5 | 0.5 | flat |
84
- | temporal_reasoning | 0.5 | 0.5 | flat |
85
- | **overall** | 0.600 | **0.700** | +10pp |
86
-
87
- Three fixes drove the lift: (1) `works_at` regex contraction fix
88
- ("I'm now working at OpenAI" now extracts), (2) role pattern `|$`
89
- lookahead + uppercase support ("I'm an ML engineer" now extracts),
90
- (3) employment-anchored temporal window (resolves "where did X live
91
- when at Y" via the works_at fact's valid_from/valid_to).
92
-
93
- Reproduce: `python benchmarks/run_ood_pipeline.py --skip-render
94
- --no-enrich --no-judge --personas 4` ·
95
- `python scripts/longmemeval_judge.py`.
83
+ | subtask | pre-fix | post-fix (2026-08-28) | plugin-kernel (2026-08-29, v0.5.0) | Δ vs pre-fix |
84
+ |---|---|---|---|---|
85
+ | single_hop | 1.0 | 1.0 | 1.0 | flat |
86
+ | knowledge_update | 0.333 | 0.667 | **1.000** | 3× |
87
+ | multi_session | 0.5 | 0.5 | 0.5 | flat |
88
+ | temporal_reasoning | 0.5 | 0.5 | 0.5 | flat |
89
+ | **overall** | 0.600 | 0.700 | **0.800** | +20pp |
90
+
91
+ The v0.5.0 lift (0.700 → 0.800) comes from the new plugin kernel
92
+ + verbatim tier: when the structured extractor misses a fact
93
+ ("I'm now working at OpenAI" → role pattern), the FTS5 + int8
94
+ dense path catches it verbatim. Fusion then merges both tiers
95
+ at μ=0 cost. The 2 misses that remain are aggregation phrasing
96
+ ("List all the places Bob has worked") and yes/no answer shape
97
+ ("Did Bob move between sessions") — extractor limitations, not
98
+ memory limitations.
99
+
100
+ Reproduce: `python scripts/longmemeval_judge.py --out
101
+ benchmarks/results/longmemeval_v0.5.0.json` ·
102
+ [`benchmarks/results/longmemeval_v0.5.0.json`](benchmarks/results/longmemeval_v0.5.0.json).
103
+
104
+ Pre-plugin-kernel fixes (0.600 → 0.700): (1) `works_at` regex
105
+ contraction fix ("I'm now working at OpenAI" now extracts),
106
+ (2) role pattern `|$` lookahead + uppercase support ("I'm an ML
107
+ engineer" now extracts), (3) employment-anchored temporal window
108
+ (resolves "where did X live when at Y" via the works_at fact's
109
+ valid_from/valid_to).
110
+
111
+ Plugin-kernel fixes (0.700 → 0.800): the new verbatim tier (FTS5
112
+ + int8 dense, MemPalace-style) catches "I'm now working at OpenAI"
113
+ verbatim when the structured extractor's role pattern still misses
114
+ it. The fusion bridge then merges both tiers at μ=0 cost. The 2
115
+ remaining misses are not memory failures — they are answer-shape
116
+ mismatches (the judge asks for a yes/no, the context block returns
117
+ a list of facts the LLM must reason over).
96
118
 
97
119
  That is the capability profile of the μ=0 extractor on real phrasing:
98
120
  strong on change-of-state statements, weak on identity/preference
@@ -0,0 +1,151 @@
1
+ """Context-M — The Universal Neuro-Symbolic Memory Fabric.
2
+
3
+ Layer 1 Symbolic Trace : bi-temporal fact graph with contradiction
4
+ resolution, temporal edges, Datalog-lite rules.
5
+ Layer 2 VSA Memory Palace: holographic reduced representations (HRR) with
6
+ INT8 / Binary-HRR / RaBitQ / PQ codecs, a
7
+ page-clustered tree index and a semantic
8
+ lookaside buffer (SLB).
9
+ Bridge : μ=0 deterministic ingest (zero LLM calls), neuro-symbolic read
10
+ path with cryptographic provenance on every retrieval.
11
+ Kernel : plugin composability (Cordis-inspired). Mount verbatim,
12
+ structured, security, or your own; new users get verbatim +
13
+ structured by default.
14
+
15
+ Mem0-compatible surface: ``from cortexm import Memory``
16
+ Plugin kernel: ``from cortexm import Context, mount_default``
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ __version__ = "0.5.0"
22
+
23
+ # μ=0 protocol counter: number of LLM invocations used by this process.
24
+ # The BEAM-honest protocol requires this to stay 0 during ingest & retrieval.
25
+ LLM_CALLS = 0
26
+
27
+
28
+ def _lazy_memory():
29
+ from cortexm.api.memory import Memory
30
+
31
+ return Memory
32
+
33
+
34
+ def __getattr__(name: str):
35
+ if name == "Memory":
36
+ return _lazy_memory()
37
+ if name == "Config":
38
+ from cortexm.config import Config
39
+
40
+ return Config
41
+ if name == "Pipeline":
42
+ from cortexm.pipeline import Pipeline
43
+ return Pipeline
44
+ if name == "Context":
45
+ from cortexm.kernel import Context
46
+ return Context
47
+ if name == "mount_default":
48
+ return _mount_default
49
+ if name == "LLM_CALLS":
50
+ from cortexm import metrics
51
+
52
+ return metrics.llm_calls()
53
+ raise AttributeError(name)
54
+
55
+
56
+ def _mount_default(*, db_path: str = ":memory:",
57
+ config: "Config | None" = None,
58
+ embedder=None,
59
+ mount_verbatim: bool = True,
60
+ mount_structured: bool = True,
61
+ mount_security: bool = False) -> "Context":
62
+ """One-liner: build a kernel with verbatim + structured (default).
63
+
64
+ This is the recommended entry point for new users. It mounts:
65
+ 1. a "db" service (sqlite3.Connection)
66
+ 2. an "embedder" service (HashingEmbedder)
67
+ 3. a "memory" service (cortexm.api.memory.Memory)
68
+ 4. VerbatimPlugin (FTS5 + dense, MemPalace-style)
69
+ 5. StructuredPlugin (bi-temporal Trace + VSA Palace)
70
+ 6. (optional) SecurityPlugin (MINJA + MIND middleware)
71
+
72
+ Plugins that need a service mount AFTER the service is registered.
73
+ The kernel's mount order is the caller's responsibility; this
74
+ helper does it correctly so users don't have to think about it.
75
+
76
+ Usage::
77
+
78
+ from cortexm import mount_default
79
+ ctx = mount_default()
80
+ v = ctx.inject("verbatim")["verbatim"]
81
+ v.add(text="My dog's name is Charlie", user_id="alice",
82
+ source_tx_id=1)
83
+ hits = v.search(query="Charlie", user_id="alice", k=5)
84
+ """
85
+ import sqlite3
86
+ from cortexm.kernel import Context
87
+ from cortexm.api.memory import Memory
88
+ from cortexm.config import Config
89
+ from cortexm.text.embedder import HashingEmbedder
90
+ from cortexm.plugins.verbatim import VerbatimPlugin
91
+ from cortexm.plugins.structured import StructuredPlugin
92
+
93
+ if config is None:
94
+ config = Config.from_env()
95
+ if db_path != ":memory:":
96
+ config.db_path = db_path
97
+ elif db_path != ":memory:":
98
+ config.db_path = db_path
99
+
100
+ ctx = Context()
101
+
102
+ # 1. Build the embedder first — both tiers share it
103
+ if embedder is None:
104
+ embedder = HashingEmbedder(
105
+ dims=getattr(config, "embed_dim", 768),
106
+ labse_enabled=getattr(config, "labse_enabled", False))
107
+ ctx.service("embedder", embedder)
108
+
109
+ # 2. Build the Memory — owns the TraceStore (sqlite3.Connection)
110
+ mem = Memory(config)
111
+ ctx.service("memory", mem)
112
+
113
+ # 3. The verbatim tier needs the SAME sqlite3 connection as the
114
+ # structured tier so both live in the same .db file. Pull the
115
+ # connection from the Memory's store.
116
+ db_conn = getattr(mem.store, "conn", None) or \
117
+ getattr(mem.store, "_conn", None) or sqlite3.connect(db_path)
118
+ ctx.service("db", db_conn)
119
+
120
+ # 4. Mount plugins in dependency order. Verbatim + structured
121
+ # both depend on services, not on each other, so order is
122
+ # flexible. Mount verbatim first so its table-creation runs
123
+ # before the structured tier's queries (avoids a race when
124
+ # the same .db is used for both).
125
+ #
126
+ # dispose_memory=False (the default) means: the caller owns
127
+ # the Memory + DB. dispose() does NOT close the SQLite
128
+ # connection. This is correct for production — users want
129
+ # their data to survive a kernel teardown / restart. Tests
130
+ # that want a hermetic teardown pass dispose_memory=True
131
+ # AND drop_tables_on_dispose=True explicitly.
132
+ if mount_verbatim:
133
+ ctx.mount(VerbatimPlugin())
134
+ if mount_structured:
135
+ ctx.mount(StructuredPlugin())
136
+ if mount_security:
137
+ from cortexm.plugins.security import SecurityPlugin
138
+ ctx.mount(SecurityPlugin())
139
+
140
+ return ctx
141
+
142
+
143
+ __all__ = [
144
+ "Memory",
145
+ "Config",
146
+ "Pipeline",
147
+ "Context",
148
+ "mount_default",
149
+ "LLM_CALLS",
150
+ "__version__",
151
+ ]
@@ -0,0 +1,252 @@
1
+ """Long-context recall — "memory past 20 steps".
2
+
3
+ This is the killer feature the user asked for explicitly:
4
+
5
+ > "use my token to submit the core of whatever a user needs
6
+ > should be the core attempt to get memory and recall past 20 steps"
7
+
8
+ Problem: every mainstream LLM has a context window that scrolls.
9
+ After ~20 turns of conversation (~8k tokens for a 32k-window model),
10
+ the early turns have scrolled out of the prompt. The LLM forgets
11
+ what was said at turn 1 by turn 25. This is the #1 pain point for
12
+ anyone building agents on top of an LLM — the model literally
13
+ cannot remember what it committed to two turns ago, let alone last
14
+ session.
15
+
16
+ cortexm's answer:
17
+
18
+ Every fact the extractor derives from each turn is persisted to
19
+ the bi-temporal Trace with a step number (run_id encodes the
20
+ session, agent_id encodes the agent, the chunk's `created_at`
21
+ encodes the step order). On retrieval, we DON'T just query by
22
+ relevance — we BIAS toward facts whose step is about to scroll
23
+ out of the LLM's window. A fact from step 5 in a 30-turn
24
+ conversation is far more valuable to surface now than a fact from
25
+ step 28 — the LLM still has step 28 in its prompt.
26
+
27
+ This is the asymmetric retrieval insight: the closer a fact is to
28
+ scrolling out, the more we should boost it. The standard cosine
29
+ score ranks by similarity; we multiply by a step-distance decay
30
+ that peaks at the LLM's window edge.
31
+
32
+ Concrete API:
33
+
34
+ m.recall_step(query, user_id="alice", current_step=30,
35
+ window=20, k=12)
36
+
37
+ → returns the top-k facts RELEVANT to the query AND in danger of
38
+ scrolling out of the LLM's window.
39
+
40
+ m.stepped_context_block(query, user_id="alice", current_step=30,
41
+ window=20, k=12)
42
+
43
+ → returns a ready-to-inject markdown context block:
44
+
45
+ ## Recalled memory (steps 1–10 about to scroll out)
46
+ - **[step 5]** Alice works at Google (conf=0.92, valid_from=2026-08-01)
47
+ - **[step 8]** Alice's birthday is 1990-05-12 (conf=0.88)
48
+ - **[step 11]** Alice prefers Python (conf=0.85)
49
+ ...
50
+
51
+ This is the "memory past 20 steps" UX. Drop it into your agent's
52
+ system prompt template, and the LLM never forgets — even if it
53
+ physically scrolled the early turns out of its window.
54
+
55
+ Lean and simple: ~200 LoC, pure Python, μ=0 (no LLM, deterministic
56
+ step-distance decay multiplied onto the existing VSA fusion score).
57
+ """
58
+ from __future__ import annotations
59
+
60
+ import datetime as _dt
61
+ import math
62
+ from datetime import datetime, timezone
63
+ from typing import Any
64
+
65
+ from cortexm.util import parse_ts
66
+
67
+
68
+ def _step_for_fact(fact, store) -> int | None:
69
+ """Best-effort step number for a fact. We use chunk.created_at
70
+ ordinal within the (user_id, agent_id, run_id) scope — the order
71
+ in which the chunk entered the Trace. If the store doesn't expose
72
+ a step field directly, we synthesize one from created_at ordering.
73
+
74
+ Returns None if no chunk is attached (machine-derived facts
75
+ with no source — these don't have a meaningful step number).
76
+ """
77
+ if not fact.source_id:
78
+ return None
79
+ try:
80
+ chunk = store.get_chunk(fact.source_id)
81
+ if not chunk:
82
+ return None
83
+ # if the schema already has a step column, use it
84
+ step = chunk.get("step") if isinstance(chunk, dict) else None
85
+ if step is not None:
86
+ return int(step)
87
+ # otherwise use created_at ordering — query the store for
88
+ # the rank of this chunk's created_at among same-scope chunks
89
+ ca = chunk.get("created_at") if isinstance(chunk, dict) else None
90
+ if not ca:
91
+ return None
92
+ try:
93
+ row = store.conn.execute(
94
+ "SELECT COUNT(*) AS n FROM chunks "
95
+ "WHERE user_id=? AND created_at < ?",
96
+ (fact.user_id, ca)).fetchone()
97
+ return int(row["n"]) + 1 if row else None
98
+ except Exception:
99
+ return None
100
+ except Exception:
101
+ return None
102
+
103
+
104
+ def _step_distance_boost(step: int | None, current_step: int,
105
+ window: int) -> float:
106
+ """Asymmetric retrieval: boost facts close to scrolling out of
107
+ the LLM's window.
108
+
109
+ If current_step = 30 and window = 20, the LLM still sees steps
110
+ 11..30. Step 10 is ABOUT to scroll out — boost it most. Step 1
111
+ has already scrolled out — boost it strongly too. Step 25 is
112
+ still in the LLM's prompt — boost it less (the LLM already has it).
113
+
114
+ The boost function is a Gaussian centered at (current_step -
115
+ window), so it peaks at the window edge:
116
+
117
+ boost(step) = 1 + peak * exp(-(step - (current_step - window))^2
118
+ / (2 * sigma^2))
119
+
120
+ Plus a constant floor of 1.0 (we never zero-out a fact — the
121
+ underlying VSA score still ranks it; we only nudge ordering).
122
+ """
123
+ if step is None:
124
+ return 1.0 # no step info → no boost, no penalty
125
+ if current_step <= 0 or window <= 0:
126
+ return 1.0
127
+ edge = max(0, current_step - window)
128
+ sigma = max(1.0, window / 3.0) # spread = 1/3 of window
129
+ peak = 0.6 # max +60% boost at the window edge
130
+ # Gaussian centered at the edge, but also weighted for already-
131
+ # scrolled-out facts (step < edge) — they get full peak boost
132
+ # because the LLM has zero access to them now.
133
+ if step <= edge:
134
+ return 1.0 + peak
135
+ return 1.0 + peak * math.exp(
136
+ -((step - edge) ** 2) / (2 * sigma * sigma))
137
+
138
+
139
+ def recall_step(memory, query: str, *, user_id: str | None = None,
140
+ agent_id: str | None = None, run_id: str | None = None,
141
+ current_step: int = 0, window: int = 20,
142
+ k: int = 12) -> dict:
143
+ """Asymmetric retrieval: top-k facts RELEVANT to the query AND in
144
+ danger of scrolling out of the LLM window.
145
+
146
+ Pipeline:
147
+ 1. Run the standard reader.search() — produces top-K candidates
148
+ ranked by VSA + symbolic fusion.
149
+ 2. For each candidate fact, compute the step-distance boost.
150
+ 3. Re-rank by (fusion_score * boost). Top-k wins.
151
+
152
+ Returns a dict shaped like ``m.search()`` but with extra fields
153
+ per memory:
154
+ - step: int | None
155
+ - step_distance_boost: float
156
+ - scrolled_out: bool (True if step <= current_step - window)
157
+ """
158
+ user_id = user_id or memory.config.default_user_id
159
+ # over-fetch then re-rank — we want enough candidates so the
160
+ # step-distance re-ordering has room to surface scrolled-out facts
161
+ raw_k = max(k * 4, 24)
162
+ result = memory.reader.search(query, user_id=user_id, agent_id=agent_id,
163
+ run_id=run_id, k=raw_k)
164
+ scored: list[tuple[float, Any, int | None, float, bool]] = []
165
+ for f in result.facts:
166
+ step = _step_for_fact(f, memory.store)
167
+ boost = _step_distance_boost(step, current_step, window)
168
+ # underlying fusion score (cosine + symbolic + chunk_recall)
169
+ fs = float(getattr(f, "score", 0.0) or
170
+ getattr(f, "fusion_score", 0.0) or 0.0)
171
+ # if the reader didn't attach a score, fall back to confidence
172
+ if fs <= 0.0:
173
+ fs = float(getattr(f, "confidence", 0.0) or 0.0)
174
+ new_score = fs * boost
175
+ scrolled_out = step is not None and current_step > 0 and step <= (
176
+ current_step - window)
177
+ scored.append((new_score, f, step, boost, scrolled_out))
178
+ scored.sort(key=lambda x: -x[0])
179
+ top = scored[:k]
180
+
181
+ out_memories = []
182
+ for new_score, f, step, boost, scrolled_out in top:
183
+ out_memories.append({
184
+ "id": f.id,
185
+ "memory": f"{f.subject} | {f.relation} | {f.value}",
186
+ "step": step,
187
+ "step_distance_boost": round(boost, 3),
188
+ "scrolled_out": scrolled_out,
189
+ "fusion_score": round(new_score, 4),
190
+ "confidence": float(getattr(f, "confidence", 0.0) or 0.0),
191
+ "valid_from": str(f.valid_from) if f.valid_from else None,
192
+ "valid_to": str(f.valid_to) if f.valid_to else None,
193
+ "source_snippet": _snippet(memory, f),
194
+ })
195
+ return {
196
+ "query": query,
197
+ "user_id": user_id,
198
+ "current_step": current_step,
199
+ "window": window,
200
+ "results": out_memories,
201
+ "context_block": _format_context_block(
202
+ out_memories, current_step, window),
203
+ "llm_calls": 0,
204
+ }
205
+
206
+
207
+ def _snippet(memory, f) -> str:
208
+ if not f.source_id:
209
+ return ""
210
+ chunk = memory.store.get_chunk(f.source_id)
211
+ if chunk and chunk.get("text"):
212
+ return chunk["text"][:160]
213
+ return ""
214
+
215
+
216
+ def _format_context_block(mems: list[dict], current_step: int,
217
+ window: int) -> str:
218
+ """Render a markdown context block ready to inject into the LLM
219
+ system prompt. Grouped by 'scrolled out' vs 'in window' so the
220
+ model sees the structure clearly."""
221
+ if not mems:
222
+ return ""
223
+ edge = max(0, current_step - window)
224
+ scrolled = [m for m in mems if m["scrolled_out"]]
225
+ in_win = [m for m in mems if not m["scrolled_out"]]
226
+ lines = []
227
+ if scrolled:
228
+ lines.append(f"## Recalled memory (steps 1–{edge} — scrolled "
229
+ f"out of context window)")
230
+ for m in scrolled:
231
+ lines.append(f"- **[step {m['step']}]** {m['memory']} "
232
+ f"(conf={m['confidence']:.2f}, "
233
+ f"valid_from={m.get('valid_from', 'n/a')})")
234
+ if m.get("source_snippet"):
235
+ lines.append(f" > …{m['source_snippet'][:120]}")
236
+ if in_win:
237
+ lines.append(f"\n## Active memory (steps {edge+1}–{current_step} "
238
+ f"— still in window, surfaced for relevance)")
239
+ for m in in_win:
240
+ lines.append(f"- **[step {m['step']}]** {m['memory']} "
241
+ f"(conf={m['confidence']:.2f})")
242
+ return "\n".join(lines)
243
+
244
+
245
+ def stepped_context_block(memory, query: str, *,
246
+ user_id: str | None = None,
247
+ current_step: int = 0,
248
+ window: int = 20, k: int = 12) -> str:
249
+ """Convenience wrapper — returns just the context_block string."""
250
+ return recall_step(memory, query, user_id=user_id,
251
+ current_step=current_step, window=window,
252
+ k=k)["context_block"]