loop-memory 0.4.19__tar.gz → 0.4.20__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 (207) hide show
  1. {loop_memory-0.4.19 → loop_memory-0.4.20}/PKG-INFO +3 -1
  2. {loop_memory-0.4.19 → loop_memory-0.4.20}/README.md +2 -0
  3. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory.egg-info/PKG-INFO +3 -1
  4. {loop_memory-0.4.19 → loop_memory-0.4.20}/pyproject.toml +1 -1
  5. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_packaging_2026_11_01.py +95 -5
  6. {loop_memory-0.4.19 → loop_memory-0.4.20}/LICENSE +0 -0
  7. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/__init__.py +0 -0
  8. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/backends/__init__.py +0 -0
  9. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/backends/embedding.py +0 -0
  10. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/backends/sentence_embedder.py +0 -0
  11. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/backends/vector_store.py +0 -0
  12. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/bench/__init__.py +0 -0
  13. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/bench/fixtures/default.json +0 -0
  14. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/bench/runner.py +0 -0
  15. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/__init__.py +0 -0
  16. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/_common.py +0 -0
  17. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/commands/__init__.py +0 -0
  18. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/commands/bench.py +0 -0
  19. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/commands/cognitive.py +0 -0
  20. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/commands/diag.py +0 -0
  21. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/commands/doctor.py +0 -0
  22. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/commands/graph.py +0 -0
  23. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/commands/hooks.py +0 -0
  24. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/commands/read.py +0 -0
  25. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/commands/rules.py +0 -0
  26. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/commands/serve.py +0 -0
  27. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/commands/write.py +0 -0
  28. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/cli/main.py +0 -0
  29. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/decorators.py +0 -0
  30. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/doctor.py +0 -0
  31. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/engine/__init__.py +0 -0
  32. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/engine/loop.py +0 -0
  33. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/engine/reflect.py +0 -0
  34. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/examples/__init__.py +0 -0
  35. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/examples/demo.py +0 -0
  36. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/export/__init__.py +0 -0
  37. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/export/memory_md.py +0 -0
  38. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/graph/__init__.py +0 -0
  39. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/graph/build.py +0 -0
  40. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/graph/extract.py +0 -0
  41. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/ingest/__init__.py +0 -0
  42. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/ingest/loader.py +0 -0
  43. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/ingest/pipeline.py +0 -0
  44. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/jobs/__init__.py +0 -0
  45. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/jobs/cognitive.py +0 -0
  46. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/jobs/compact.py +0 -0
  47. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/jobs/consolidate.py +0 -0
  48. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/jobs/contradiction.py +0 -0
  49. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/jobs/dream.py +0 -0
  50. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/jobs/evolution.py +0 -0
  51. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/jobs/graph.py +0 -0
  52. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/jobs/llm_compact_pass.py +0 -0
  53. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/jobs/llm_consolidate.py +0 -0
  54. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/jobs/scheduler.py +0 -0
  55. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/llm/__init__.py +0 -0
  56. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/llm/base.py +0 -0
  57. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/llm/openai_adapter.py +0 -0
  58. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/llm/providers.py +0 -0
  59. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/mcp/__init__.py +0 -0
  60. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/memory/__init__.py +0 -0
  61. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/memory/types.py +0 -0
  62. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/privacy/__init__.py +0 -0
  63. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/privacy/private.py +0 -0
  64. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/privacy/redact.py +0 -0
  65. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/py.typed +0 -0
  66. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/sdk.py +0 -0
  67. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/sdk_extensions.py +0 -0
  68. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/security/__init__.py +0 -0
  69. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/security/secrets.py +0 -0
  70. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/__init__.py +0 -0
  71. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/app.py +0 -0
  72. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/handlers.py +0 -0
  73. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/routes/_shared.py +0 -0
  74. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/routes/admin.py +0 -0
  75. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/routes/cognitive.py +0 -0
  76. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/routes/export.py +0 -0
  77. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/routes/graph.py +0 -0
  78. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/routes/insights.py +0 -0
  79. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/routes/memories.py +0 -0
  80. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/routes/sessions.py +0 -0
  81. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/routes/system.py +0 -0
  82. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/routes/wiki.py +0 -0
  83. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/__init__.py +0 -0
  84. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/css/app.css +0 -0
  85. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/css/layout.css +0 -0
  86. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/i18n/en.json +0 -0
  87. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/i18n/zh.json +0 -0
  88. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/img/logo-light.svg +0 -0
  89. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/img/logo-mark.svg +0 -0
  90. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/img/logo.svg +0 -0
  91. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/index.html +0 -0
  92. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/App.js +0 -0
  93. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/api.js +0 -0
  94. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/Dashboard.js +0 -0
  95. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/Diagnostic.js +0 -0
  96. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/IngestPopover.js +0 -0
  97. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/KnowledgeGraph.js +0 -0
  98. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/RingMeter.js +0 -0
  99. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/RunStrip.js +0 -0
  100. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/SectionTitle.js +0 -0
  101. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/Settings.js +0 -0
  102. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/Sidebar.js +0 -0
  103. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/Tabs.js +0 -0
  104. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/Timeline.js +0 -0
  105. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/Toast.js +0 -0
  106. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/TopBar.js +0 -0
  107. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/Wiki.js +0 -0
  108. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/components/WikiEditor.js +0 -0
  109. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/composables/useI18n.js +0 -0
  110. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/lib/sanitize.js +0 -0
  111. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/lib/vue.esm-browser.prod.js +0 -0
  112. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/main.js +0 -0
  113. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/static/js/store.js +0 -0
  114. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/serve/watcher.py +0 -0
  115. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/storage/__init__.py +0 -0
  116. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/storage/lexical.py +0 -0
  117. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/storage/retrieval.py +0 -0
  118. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/storage/snapshot.py +0 -0
  119. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/storage/sqlite_store.py +0 -0
  120. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/wiki/__init__.py +0 -0
  121. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/wiki/backfill.py +0 -0
  122. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/wiki/classifier.py +0 -0
  123. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/wiki/prompts.py +0 -0
  124. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory/wiki/scope.py +0 -0
  125. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory.egg-info/SOURCES.txt +0 -0
  126. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory.egg-info/dependency_links.txt +0 -0
  127. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory.egg-info/entry_points.txt +0 -0
  128. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory.egg-info/requires.txt +0 -0
  129. {loop_memory-0.4.19 → loop_memory-0.4.20}/loop_memory.egg-info/top_level.txt +0 -0
  130. {loop_memory-0.4.19 → loop_memory-0.4.20}/setup.cfg +0 -0
  131. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_admin_ingest_route.py +0 -0
  132. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_agent_memory_api.py +0 -0
  133. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_agent_memory_sdk.py +0 -0
  134. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_agents_init.py +0 -0
  135. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_audit_triggers_2026_09_27.py +0 -0
  136. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_auth_token_rotate.py +0 -0
  137. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_bench_2026_11_01.py +0 -0
  138. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_busy_timeout_2026_09_24.py +0 -0
  139. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cache_cap_2026_09_23.py +0 -0
  140. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cli_audit_supersede.py +0 -0
  141. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cli_export_okf.py +0 -0
  142. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cli_init.py +0 -0
  143. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cli_memory_stats.py +0 -0
  144. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cli_recall_as_of.py +0 -0
  145. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cli_recall_paths.py +0 -0
  146. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cli_recall_verbose.py +0 -0
  147. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cli_rules.py +0 -0
  148. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cli_snapshot.py +0 -0
  149. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cli_v7.py +0 -0
  150. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_cli_version.py +0 -0
  151. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_content_hash_dedup_2026_10_18.py +0 -0
  152. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_contradictions.py +0 -0
  153. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_decorators_2026_10_18.py +0 -0
  154. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_doctor_2026_10_12.py +0 -0
  155. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_dream_2026_10_25.py +0 -0
  156. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_dream_cli_http_2026_10_25.py +0 -0
  157. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_evolution.py +0 -0
  158. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_evolution_quality.py +0 -0
  159. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_expanduser.py +0 -0
  160. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_export_ask.py +0 -0
  161. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_export_okf.py +0 -0
  162. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_graph.py +0 -0
  163. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_healthz_readyz_2026_09_24.py +0 -0
  164. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_import_okf_2026_09_27.py +0 -0
  165. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_ingest.py +0 -0
  166. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_latest_only_2026_10_25.py +0 -0
  167. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_lexical_ranking_2026_11_01.py +0 -0
  168. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_llm_consolidator.py +0 -0
  169. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_llm_fingerprint_not_persisted.py +0 -0
  170. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_llm_providers.py +0 -0
  171. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_llm_test_endpoint.py +0 -0
  172. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_loop.py +0 -0
  173. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_mcp.py +0 -0
  174. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_memories_pagination.py +0 -0
  175. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_memory_stats.py +0 -0
  176. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_openclaw_loader.py +0 -0
  177. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_package_version.py +0 -0
  178. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_path_safety_2026_09_23.py +0 -0
  179. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_recall_as_of.py +0 -0
  180. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_recall_budget_2026_10_05.py +0 -0
  181. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_recall_paths.py +0 -0
  182. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_recall_provenance.py +0 -0
  183. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_recall_validity_http_2026_10_18.py +0 -0
  184. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_reflection.py +0 -0
  185. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_repo_framing.py +0 -0
  186. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_retrieval_channels_2026_10_18.py +0 -0
  187. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_routes_audit_2026_09_13.py +0 -0
  188. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_routes_audit_2026_09_20.py +0 -0
  189. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_score_api.py +0 -0
  190. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_scoring_v2.py +0 -0
  191. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_secrets.py +0 -0
  192. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_serve_app.py +0 -0
  193. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_serve_handlers.py +0 -0
  194. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_session_order.py +0 -0
  195. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_short_query.py +0 -0
  196. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_snapshot.py +0 -0
  197. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_store.py +0 -0
  198. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_subgraph_hops_2026_09_23.py +0 -0
  199. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_subject_key_2026_10_25.py +0 -0
  200. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_summarization.py +0 -0
  201. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_supersession_chain.py +0 -0
  202. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_universal_memory.py +0 -0
  203. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_vector_store.py +0 -0
  204. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_watcher.py +0 -0
  205. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_wiki_classifier.py +0 -0
  206. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_wiki_export_escape.py +0 -0
  207. {loop_memory-0.4.19 → loop_memory-0.4.20}/tests/test_wiki_prompts.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: loop-memory
3
- Version: 0.4.19
3
+ Version: 0.4.20
4
4
  Summary: A general-purpose, local memory system for every AI agent you run. Loop Memory auto-captures conversations from Codex / Claude / Hermes / OpenClaw, scores them by importance × recency × usage × feedback, distils them into a curated wiki, and serves everything from a single web UI.
5
5
  Author: Loop Memory contributors <loop-memory@users.noreply.github.com>
6
6
  License-Expression: MIT
@@ -67,6 +67,8 @@ Dynamic: license-file
67
67
 
68
68
  ---
69
69
 
70
+ > **What's new in 0.4.20** — **the packaging test works on the oldest supported Python** (audit 2026-11-01, patch follow-up to 0.4.19). The 0.4.19 packaging test parsed `pyproject.toml` with `tomllib`, which is stdlib only from Python **3.11** onwards, while the project declares `requires-python = ">=3.10"` — so the 3.10 CI job failed with `ModuleNotFoundError: No module named 'tomllib'` while 3.11 and 3.12 passed, the classic shape of a bug that only bites the oldest supported interpreter. The check now reads the `[tool.setuptools.package-data]` table directly (it is a flat mapping of package name to a list of glob strings), and a new case runs it in a subprocess with `tomllib` blocked at the import system level, so the 3.10 path is exercised on every version rather than only on the 3.10 runner. No behaviour change — the 0.4.19 distribution is unchanged and correct. 1 new regression case; suite at **1004 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0420---2026-11-01)
71
+
70
72
  > **What's new in 0.4.19** — **the PyPI wheel now ships the whole dashboard and the benchmark fixture** (audit 2026-11-01, packaging follow-up to 0.4.18). The 0.4.18 wheel contained exactly two data files — `loop_memory/py.typed` and `loop_memory/serve/static/index.html` — because the package-data declaration was `loop_memory = ["py.typed", "serve/static/*.html"]`, and that glob is **not recursive**. Every CSS, JavaScript, i18n and image asset and the new benchmark fixture were **missing from the distribution**. Reproduced against the published wheel: `pip install loop-memory` started a server whose `GET /` returned `200` but whose `GET /static/js/main.js` and `GET /static/css/app.css` both returned **404** — a blank dashboard — and `loop-memory bench` crashed with `FileNotFoundError` on the packaged fixture. The declaration is now per package with recursive globs (`"loop_memory.serve.static" = ["**/*.html", "**/*.css", "**/*.js", "**/*.json", "**/*.svg"]`, `"loop_memory.bench" = ["fixtures/*.json"]`), so a new asset is picked up without anyone remembering to extend it, and the wheel contains all 32 non-Python files in the package tree. A new packaging test builds the real wheel and sdist **from a pristine source copy** — setuptools caches its file list in `egg-info/SOURCES.txt` and packages a stale `build/lib`, so building in the working tree would make the test pass for the wrong reason — and asserts every non-Python file actually reaches the distribution, then unpacks the wheel and runs the benchmark out of it. 8 new regression cases; suite at **1003 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0419---2026-11-01)
71
73
 
72
74
  > **What's new in 0.4.18** — **the BM25 channel no longer publishes a score that collapses to zero, and there is finally a benchmark that measures it** (audit 2026-11-01, weekly ecosystem sync). SQLite's FTS5 `bm25()` computes the classic idf `log((N - n + 0.5) / (n + 0.5))` and **clamps every non-positive idf to `1e-6`** — so a query term present in 49 % of the corpus keeps a real idf while one present in 51 % has its weight annihilated, a factor of ~40 000 between two almost equally discriminative terms. With the `trigram` tokenizer that is the *normal* case for CJK (`知识图` alone occurs in more than half of any knowledge-graph-heavy corpus), and the visible symptom was the 0.4.15 evidence chain reporting **`bm25: 0.0`** — "this hit because bm25 0.0". The published score is now a textbook BM25 (`k1 = 1.2`, `b = 0.75`) with the Lucene / BM25+ idf `log(1 + (N - n + 0.5) / (n + 0.5))`, which is strictly positive for every term, so the scale cannot collapse. **A document that contains the query verbatim now outranks one that merely contains the same scattered trigrams** — the transferable half of `rohitg00/agentmemory` v0.9.31's prefix-score fix (2026-10-26: strict recall-all@5 **65.74 % → 84.68 %**, +18.94 pp), whose release note is careful to call its numbers "component retrieval results on inspected development data, not held-out answer accuracy". The `LIKE` fallback and the FTS5 hits are finally on the **same scale** (a `LIKE`-only hit used to publish the memory's `importance` under the name `bm25`). The candidate set is unchanged — the fix re-ranks, it does not add or drop documents. New **`loop-memory bench`**: a deterministic retrieval-quality benchmark over a fixed, versioned fixture (30 memories, 12 wiki pages, 11 judged queries) reporting **recall@1/3/5, MRR@10, nDCG@10** with the fixture's revision and the SHA-256 of its bytes; the run is fully offline (**no provider API is called, $0, no network**) and builds its store in a throwaway directory. On the shipped fixture the channel scored **MRR@10 = 0.6061, recall@1 = 0.2273, nDCG@10 = 0.7078** before and **1.0000 / 0.9545 / 1.0000** after — and the report keeps the raw FTS5 score next to the repaired one, so the defect stays visible instead of being quietly fixed and forgotten. Source: *agentmemory* v0.9.31 evaluation methodology + the FTS5 idf clamp in SQLite's `fts5_aux.c` (Apache-2.0 — *pattern only, no code copied*); honcho v3.4.1's "a filter must not silently drop a term" and v3.5.0's "a conclusion must cite its source" were reviewed and need no code (AGPL-3.0 — *pattern only*). 37 new regression cases; suite at **995 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0418---2026-11-01)
@@ -22,6 +22,8 @@
22
22
 
23
23
  ---
24
24
 
25
+ > **What's new in 0.4.20** — **the packaging test works on the oldest supported Python** (audit 2026-11-01, patch follow-up to 0.4.19). The 0.4.19 packaging test parsed `pyproject.toml` with `tomllib`, which is stdlib only from Python **3.11** onwards, while the project declares `requires-python = ">=3.10"` — so the 3.10 CI job failed with `ModuleNotFoundError: No module named 'tomllib'` while 3.11 and 3.12 passed, the classic shape of a bug that only bites the oldest supported interpreter. The check now reads the `[tool.setuptools.package-data]` table directly (it is a flat mapping of package name to a list of glob strings), and a new case runs it in a subprocess with `tomllib` blocked at the import system level, so the 3.10 path is exercised on every version rather than only on the 3.10 runner. No behaviour change — the 0.4.19 distribution is unchanged and correct. 1 new regression case; suite at **1004 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0420---2026-11-01)
26
+
25
27
  > **What's new in 0.4.19** — **the PyPI wheel now ships the whole dashboard and the benchmark fixture** (audit 2026-11-01, packaging follow-up to 0.4.18). The 0.4.18 wheel contained exactly two data files — `loop_memory/py.typed` and `loop_memory/serve/static/index.html` — because the package-data declaration was `loop_memory = ["py.typed", "serve/static/*.html"]`, and that glob is **not recursive**. Every CSS, JavaScript, i18n and image asset and the new benchmark fixture were **missing from the distribution**. Reproduced against the published wheel: `pip install loop-memory` started a server whose `GET /` returned `200` but whose `GET /static/js/main.js` and `GET /static/css/app.css` both returned **404** — a blank dashboard — and `loop-memory bench` crashed with `FileNotFoundError` on the packaged fixture. The declaration is now per package with recursive globs (`"loop_memory.serve.static" = ["**/*.html", "**/*.css", "**/*.js", "**/*.json", "**/*.svg"]`, `"loop_memory.bench" = ["fixtures/*.json"]`), so a new asset is picked up without anyone remembering to extend it, and the wheel contains all 32 non-Python files in the package tree. A new packaging test builds the real wheel and sdist **from a pristine source copy** — setuptools caches its file list in `egg-info/SOURCES.txt` and packages a stale `build/lib`, so building in the working tree would make the test pass for the wrong reason — and asserts every non-Python file actually reaches the distribution, then unpacks the wheel and runs the benchmark out of it. 8 new regression cases; suite at **1003 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0419---2026-11-01)
26
28
 
27
29
  > **What's new in 0.4.18** — **the BM25 channel no longer publishes a score that collapses to zero, and there is finally a benchmark that measures it** (audit 2026-11-01, weekly ecosystem sync). SQLite's FTS5 `bm25()` computes the classic idf `log((N - n + 0.5) / (n + 0.5))` and **clamps every non-positive idf to `1e-6`** — so a query term present in 49 % of the corpus keeps a real idf while one present in 51 % has its weight annihilated, a factor of ~40 000 between two almost equally discriminative terms. With the `trigram` tokenizer that is the *normal* case for CJK (`知识图` alone occurs in more than half of any knowledge-graph-heavy corpus), and the visible symptom was the 0.4.15 evidence chain reporting **`bm25: 0.0`** — "this hit because bm25 0.0". The published score is now a textbook BM25 (`k1 = 1.2`, `b = 0.75`) with the Lucene / BM25+ idf `log(1 + (N - n + 0.5) / (n + 0.5))`, which is strictly positive for every term, so the scale cannot collapse. **A document that contains the query verbatim now outranks one that merely contains the same scattered trigrams** — the transferable half of `rohitg00/agentmemory` v0.9.31's prefix-score fix (2026-10-26: strict recall-all@5 **65.74 % → 84.68 %**, +18.94 pp), whose release note is careful to call its numbers "component retrieval results on inspected development data, not held-out answer accuracy". The `LIKE` fallback and the FTS5 hits are finally on the **same scale** (a `LIKE`-only hit used to publish the memory's `importance` under the name `bm25`). The candidate set is unchanged — the fix re-ranks, it does not add or drop documents. New **`loop-memory bench`**: a deterministic retrieval-quality benchmark over a fixed, versioned fixture (30 memories, 12 wiki pages, 11 judged queries) reporting **recall@1/3/5, MRR@10, nDCG@10** with the fixture's revision and the SHA-256 of its bytes; the run is fully offline (**no provider API is called, $0, no network**) and builds its store in a throwaway directory. On the shipped fixture the channel scored **MRR@10 = 0.6061, recall@1 = 0.2273, nDCG@10 = 0.7078** before and **1.0000 / 0.9545 / 1.0000** after — and the report keeps the raw FTS5 score next to the repaired one, so the defect stays visible instead of being quietly fixed and forgotten. Source: *agentmemory* v0.9.31 evaluation methodology + the FTS5 idf clamp in SQLite's `fts5_aux.c` (Apache-2.0 — *pattern only, no code copied*); honcho v3.4.1's "a filter must not silently drop a term" and v3.5.0's "a conclusion must cite its source" were reviewed and need no code (AGPL-3.0 — *pattern only*). 37 new regression cases; suite at **995 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0418---2026-11-01)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: loop-memory
3
- Version: 0.4.19
3
+ Version: 0.4.20
4
4
  Summary: A general-purpose, local memory system for every AI agent you run. Loop Memory auto-captures conversations from Codex / Claude / Hermes / OpenClaw, scores them by importance × recency × usage × feedback, distils them into a curated wiki, and serves everything from a single web UI.
5
5
  Author: Loop Memory contributors <loop-memory@users.noreply.github.com>
6
6
  License-Expression: MIT
@@ -67,6 +67,8 @@ Dynamic: license-file
67
67
 
68
68
  ---
69
69
 
70
+ > **What's new in 0.4.20** — **the packaging test works on the oldest supported Python** (audit 2026-11-01, patch follow-up to 0.4.19). The 0.4.19 packaging test parsed `pyproject.toml` with `tomllib`, which is stdlib only from Python **3.11** onwards, while the project declares `requires-python = ">=3.10"` — so the 3.10 CI job failed with `ModuleNotFoundError: No module named 'tomllib'` while 3.11 and 3.12 passed, the classic shape of a bug that only bites the oldest supported interpreter. The check now reads the `[tool.setuptools.package-data]` table directly (it is a flat mapping of package name to a list of glob strings), and a new case runs it in a subprocess with `tomllib` blocked at the import system level, so the 3.10 path is exercised on every version rather than only on the 3.10 runner. No behaviour change — the 0.4.19 distribution is unchanged and correct. 1 new regression case; suite at **1004 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0420---2026-11-01)
71
+
70
72
  > **What's new in 0.4.19** — **the PyPI wheel now ships the whole dashboard and the benchmark fixture** (audit 2026-11-01, packaging follow-up to 0.4.18). The 0.4.18 wheel contained exactly two data files — `loop_memory/py.typed` and `loop_memory/serve/static/index.html` — because the package-data declaration was `loop_memory = ["py.typed", "serve/static/*.html"]`, and that glob is **not recursive**. Every CSS, JavaScript, i18n and image asset and the new benchmark fixture were **missing from the distribution**. Reproduced against the published wheel: `pip install loop-memory` started a server whose `GET /` returned `200` but whose `GET /static/js/main.js` and `GET /static/css/app.css` both returned **404** — a blank dashboard — and `loop-memory bench` crashed with `FileNotFoundError` on the packaged fixture. The declaration is now per package with recursive globs (`"loop_memory.serve.static" = ["**/*.html", "**/*.css", "**/*.js", "**/*.json", "**/*.svg"]`, `"loop_memory.bench" = ["fixtures/*.json"]`), so a new asset is picked up without anyone remembering to extend it, and the wheel contains all 32 non-Python files in the package tree. A new packaging test builds the real wheel and sdist **from a pristine source copy** — setuptools caches its file list in `egg-info/SOURCES.txt` and packages a stale `build/lib`, so building in the working tree would make the test pass for the wrong reason — and asserts every non-Python file actually reaches the distribution, then unpacks the wheel and runs the benchmark out of it. 8 new regression cases; suite at **1003 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0419---2026-11-01)
71
73
 
72
74
  > **What's new in 0.4.18** — **the BM25 channel no longer publishes a score that collapses to zero, and there is finally a benchmark that measures it** (audit 2026-11-01, weekly ecosystem sync). SQLite's FTS5 `bm25()` computes the classic idf `log((N - n + 0.5) / (n + 0.5))` and **clamps every non-positive idf to `1e-6`** — so a query term present in 49 % of the corpus keeps a real idf while one present in 51 % has its weight annihilated, a factor of ~40 000 between two almost equally discriminative terms. With the `trigram` tokenizer that is the *normal* case for CJK (`知识图` alone occurs in more than half of any knowledge-graph-heavy corpus), and the visible symptom was the 0.4.15 evidence chain reporting **`bm25: 0.0`** — "this hit because bm25 0.0". The published score is now a textbook BM25 (`k1 = 1.2`, `b = 0.75`) with the Lucene / BM25+ idf `log(1 + (N - n + 0.5) / (n + 0.5))`, which is strictly positive for every term, so the scale cannot collapse. **A document that contains the query verbatim now outranks one that merely contains the same scattered trigrams** — the transferable half of `rohitg00/agentmemory` v0.9.31's prefix-score fix (2026-10-26: strict recall-all@5 **65.74 % → 84.68 %**, +18.94 pp), whose release note is careful to call its numbers "component retrieval results on inspected development data, not held-out answer accuracy". The `LIKE` fallback and the FTS5 hits are finally on the **same scale** (a `LIKE`-only hit used to publish the memory's `importance` under the name `bm25`). The candidate set is unchanged — the fix re-ranks, it does not add or drop documents. New **`loop-memory bench`**: a deterministic retrieval-quality benchmark over a fixed, versioned fixture (30 memories, 12 wiki pages, 11 judged queries) reporting **recall@1/3/5, MRR@10, nDCG@10** with the fixture's revision and the SHA-256 of its bytes; the run is fully offline (**no provider API is called, $0, no network**) and builds its store in a throwaway directory. On the shipped fixture the channel scored **MRR@10 = 0.6061, recall@1 = 0.2273, nDCG@10 = 0.7078** before and **1.0000 / 0.9545 / 1.0000** after — and the report keeps the raw FTS5 score next to the repaired one, so the defect stays visible instead of being quietly fixed and forgotten. Source: *agentmemory* v0.9.31 evaluation methodology + the FTS5 idf clamp in SQLite's `fts5_aux.c` (Apache-2.0 — *pattern only, no code copied*); honcho v3.4.1's "a filter must not silently drop a term" and v3.5.0's "a conclusion must cite its source" were reviewed and need no code (AGPL-3.0 — *pattern only*). 37 new regression cases; suite at **995 passed / 4 skipped**. [Full CHANGELOG →](./CHANGELOG.md#0418---2026-11-01)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "loop-memory"
7
- version = "0.4.19"
7
+ version = "0.4.20"
8
8
  description = "A general-purpose, local memory system for every AI agent you run. Loop Memory auto-captures conversations from Codex / Claude / Hermes / OpenClaw, scores them by importance × recency × usage × feedback, distils them into a curated wiki, and serves everything from a single web UI."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -28,10 +28,12 @@ from __future__ import annotations
28
28
 
29
29
  import hashlib
30
30
  import os
31
+ import re
31
32
  import shutil
32
33
  import subprocess
33
34
  import sys
34
35
  import tarfile
36
+ import textwrap
35
37
  import zipfile
36
38
  from pathlib import Path
37
39
 
@@ -191,18 +193,106 @@ def test_sdist_ships_the_data_files(built_sdist):
191
193
  assert f"{prefix}/{rel}" in names, f"{rel} missing from the sdist"
192
194
 
193
195
 
196
+ def _parse_package_data(text: str) -> dict[str, list[str]]:
197
+ """Parse the ``[tool.setuptools.package-data]`` table.
198
+
199
+ ``tomllib`` is stdlib only from Python 3.11 and this project
200
+ supports 3.10, so the table is read directly. It is a flat
201
+ mapping of package name to a list of glob strings -- small enough
202
+ that a TOML dependency would cost more than it saves, and the CI
203
+ matrix runs 3.10 / 3.11 / 3.12.
204
+ """
205
+ out: dict[str, list[str]] = {}
206
+ in_table = False
207
+ key: str | None = None
208
+ buf = ""
209
+
210
+ def flush() -> None:
211
+ nonlocal key, buf
212
+ if key is not None:
213
+ out[key] = re.findall(r'"([^"]*)"', buf)
214
+ key = None
215
+ buf = ""
216
+
217
+ for line in text.splitlines():
218
+ stripped = line.strip()
219
+ if key is not None:
220
+ buf += " " + stripped
221
+ if buf.count("[") <= buf.count("]"):
222
+ flush()
223
+ continue
224
+ if not stripped or stripped.startswith("#"):
225
+ continue
226
+ if stripped.startswith("["):
227
+ in_table = stripped == "[tool.setuptools.package-data]"
228
+ continue
229
+ if not in_table:
230
+ continue
231
+ name, _, value = stripped.partition("=")
232
+ key = name.strip().strip('"')
233
+ buf = value.strip()
234
+ if buf.count("[") <= buf.count("]"):
235
+ flush()
236
+ flush()
237
+ return out
238
+
239
+
194
240
  def test_pyproject_declares_the_data_packages():
195
241
  """The declaration itself -- so the fix is readable, not implicit."""
196
- import tomllib
197
-
198
- with open(REPO_ROOT / "pyproject.toml", "rb") as fh:
199
- data = tomllib.load(fh)
200
- package_data = data["tool"]["setuptools"]["package-data"]
242
+ package_data = _parse_package_data(
243
+ (REPO_ROOT / "pyproject.toml").read_text("utf-8")
244
+ )
201
245
  assert "py.typed" in package_data["loop_memory"]
202
246
  assert "loop_memory.bench" in package_data
203
247
  assert "loop_memory.serve.static" in package_data
204
248
 
205
249
 
250
+ def test_the_declaration_check_runs_without_tomllib():
251
+ """Python 3.10 has no ``tomllib`` -- the check must not need one.
252
+
253
+ The 3.10 CI job failed on exactly this: the first version of the
254
+ test imported ``tomllib``, which only exists from 3.11 onwards,
255
+ while ``pyproject.toml`` declares ``requires-python = ">=3.10"``.
256
+ """
257
+ code = textwrap.dedent(
258
+ """
259
+ import importlib.util
260
+ import pathlib
261
+ import sys
262
+
263
+ class Blocker:
264
+ def find_spec(self, name, path=None, target=None):
265
+ if name == "tomllib":
266
+ raise ImportError("tomllib is Python 3.11+")
267
+ return None
268
+
269
+ sys.meta_path.insert(0, Blocker())
270
+ sys.modules.pop("tomllib", None)
271
+
272
+ root = pathlib.Path({root!r})
273
+ spec = importlib.util.spec_from_file_location(
274
+ "lm_packaging_probe", root / "tests" / "test_packaging_2026_11_01.py"
275
+ )
276
+ module = importlib.util.module_from_spec(spec)
277
+ spec.loader.exec_module(module)
278
+ data = module._parse_package_data(
279
+ (root / "pyproject.toml").read_text("utf-8")
280
+ )
281
+ assert "loop_memory.bench" in data, data
282
+ assert "loop_memory.serve.static" in data, data
283
+ print("ok")
284
+ """
285
+ ).format(root=str(REPO_ROOT))
286
+ proc = subprocess.run(
287
+ [sys.executable, "-c", code],
288
+ capture_output=True,
289
+ text=True,
290
+ timeout=120,
291
+ )
292
+ assert proc.returncode == 0, proc.stderr
293
+ assert "ok" in proc.stdout
294
+
295
+
206
296
  # --------------------------------------------------------------------------
207
297
  # End-to-end: run the bench out of an unpacked wheel
208
298
  # --------------------------------------------------------------------------
File without changes
File without changes