superlocalmemory 2.8.6 → 3.0.1

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 (431) hide show
  1. package/LICENSE +9 -1
  2. package/NOTICE +63 -0
  3. package/README.md +165 -480
  4. package/bin/slm +17 -449
  5. package/bin/slm-npm +62 -48
  6. package/conftest.py +5 -0
  7. package/docs/api-reference.md +284 -0
  8. package/docs/architecture.md +149 -0
  9. package/docs/auto-memory.md +150 -0
  10. package/docs/cli-reference.md +276 -0
  11. package/docs/compliance.md +191 -0
  12. package/docs/configuration.md +182 -0
  13. package/docs/getting-started.md +102 -0
  14. package/docs/ide-setup.md +261 -0
  15. package/docs/mcp-tools.md +220 -0
  16. package/docs/migration-from-v2.md +170 -0
  17. package/docs/profiles.md +173 -0
  18. package/docs/troubleshooting.md +310 -0
  19. package/{configs → ide/configs}/antigravity-mcp.json +3 -3
  20. package/ide/configs/chatgpt-desktop-mcp.json +16 -0
  21. package/{configs → ide/configs}/claude-desktop-mcp.json +3 -3
  22. package/{configs → ide/configs}/codex-mcp.toml +4 -4
  23. package/{configs → ide/configs}/continue-mcp.yaml +4 -3
  24. package/{configs → ide/configs}/continue-skills.yaml +6 -6
  25. package/ide/configs/cursor-mcp.json +15 -0
  26. package/{configs → ide/configs}/gemini-cli-mcp.json +2 -2
  27. package/{configs → ide/configs}/jetbrains-mcp.json +2 -2
  28. package/{configs → ide/configs}/opencode-mcp.json +2 -2
  29. package/{configs → ide/configs}/perplexity-mcp.json +2 -2
  30. package/{configs → ide/configs}/vscode-copilot-mcp.json +2 -2
  31. package/{configs → ide/configs}/windsurf-mcp.json +3 -3
  32. package/{configs → ide/configs}/zed-mcp.json +2 -2
  33. package/{hooks → ide/hooks}/context-hook.js +9 -20
  34. package/ide/hooks/memory-list-skill.js +70 -0
  35. package/ide/hooks/memory-profile-skill.js +101 -0
  36. package/ide/hooks/memory-recall-skill.js +62 -0
  37. package/ide/hooks/memory-remember-skill.js +68 -0
  38. package/ide/hooks/memory-reset-skill.js +160 -0
  39. package/{hooks → ide/hooks}/post-recall-hook.js +2 -2
  40. package/ide/integrations/langchain/README.md +106 -0
  41. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +9 -0
  42. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +201 -0
  43. package/ide/integrations/langchain/pyproject.toml +38 -0
  44. package/{src/learning → ide/integrations/langchain}/tests/__init__.py +1 -0
  45. package/ide/integrations/langchain/tests/test_chat_message_history.py +215 -0
  46. package/ide/integrations/langchain/tests/test_security.py +117 -0
  47. package/ide/integrations/llamaindex/README.md +81 -0
  48. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +9 -0
  49. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +316 -0
  50. package/ide/integrations/llamaindex/pyproject.toml +43 -0
  51. package/{src/lifecycle → ide/integrations/llamaindex}/tests/__init__.py +1 -2
  52. package/ide/integrations/llamaindex/tests/test_chat_store.py +294 -0
  53. package/ide/integrations/llamaindex/tests/test_security.py +241 -0
  54. package/{skills → ide/skills}/slm-build-graph/SKILL.md +6 -6
  55. package/{skills → ide/skills}/slm-list-recent/SKILL.md +5 -5
  56. package/{skills → ide/skills}/slm-recall/SKILL.md +5 -5
  57. package/{skills → ide/skills}/slm-remember/SKILL.md +6 -6
  58. package/{skills → ide/skills}/slm-show-patterns/SKILL.md +7 -7
  59. package/{skills → ide/skills}/slm-status/SKILL.md +9 -9
  60. package/{skills → ide/skills}/slm-switch-profile/SKILL.md +9 -9
  61. package/package.json +13 -22
  62. package/pyproject.toml +85 -0
  63. package/scripts/build-dmg.sh +417 -0
  64. package/scripts/install-skills.ps1 +334 -0
  65. package/scripts/postinstall.js +2 -2
  66. package/scripts/start-dashboard.ps1 +52 -0
  67. package/scripts/start-dashboard.sh +41 -0
  68. package/scripts/sync-wiki.ps1 +127 -0
  69. package/scripts/sync-wiki.sh +82 -0
  70. package/scripts/test-dmg.sh +161 -0
  71. package/scripts/test-npm-package.ps1 +252 -0
  72. package/scripts/test-npm-package.sh +207 -0
  73. package/scripts/verify-install.ps1 +294 -0
  74. package/scripts/verify-install.sh +266 -0
  75. package/src/superlocalmemory/__init__.py +0 -0
  76. package/src/superlocalmemory/attribution/__init__.py +9 -0
  77. package/src/superlocalmemory/attribution/mathematical_dna.py +235 -0
  78. package/src/superlocalmemory/attribution/signer.py +153 -0
  79. package/src/superlocalmemory/attribution/watermark.py +189 -0
  80. package/src/superlocalmemory/cli/__init__.py +5 -0
  81. package/src/superlocalmemory/cli/commands.py +245 -0
  82. package/src/superlocalmemory/cli/main.py +89 -0
  83. package/src/superlocalmemory/cli/migrate_cmd.py +55 -0
  84. package/src/superlocalmemory/cli/post_install.py +99 -0
  85. package/src/superlocalmemory/cli/setup_wizard.py +129 -0
  86. package/src/superlocalmemory/compliance/__init__.py +0 -0
  87. package/src/superlocalmemory/compliance/abac.py +204 -0
  88. package/src/superlocalmemory/compliance/audit.py +314 -0
  89. package/src/superlocalmemory/compliance/eu_ai_act.py +131 -0
  90. package/src/superlocalmemory/compliance/gdpr.py +294 -0
  91. package/src/superlocalmemory/compliance/lifecycle.py +158 -0
  92. package/src/superlocalmemory/compliance/retention.py +232 -0
  93. package/src/superlocalmemory/compliance/scheduler.py +148 -0
  94. package/src/superlocalmemory/core/__init__.py +0 -0
  95. package/src/superlocalmemory/core/config.py +391 -0
  96. package/src/superlocalmemory/core/embeddings.py +293 -0
  97. package/src/superlocalmemory/core/engine.py +701 -0
  98. package/src/superlocalmemory/core/hooks.py +65 -0
  99. package/src/superlocalmemory/core/maintenance.py +172 -0
  100. package/src/superlocalmemory/core/modes.py +140 -0
  101. package/src/superlocalmemory/core/profiles.py +234 -0
  102. package/src/superlocalmemory/core/registry.py +117 -0
  103. package/src/superlocalmemory/dynamics/__init__.py +0 -0
  104. package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +223 -0
  105. package/src/superlocalmemory/encoding/__init__.py +0 -0
  106. package/src/superlocalmemory/encoding/consolidator.py +485 -0
  107. package/src/superlocalmemory/encoding/emotional.py +125 -0
  108. package/src/superlocalmemory/encoding/entity_resolver.py +525 -0
  109. package/src/superlocalmemory/encoding/entropy_gate.py +104 -0
  110. package/src/superlocalmemory/encoding/fact_extractor.py +775 -0
  111. package/src/superlocalmemory/encoding/foresight.py +91 -0
  112. package/src/superlocalmemory/encoding/graph_builder.py +302 -0
  113. package/src/superlocalmemory/encoding/observation_builder.py +160 -0
  114. package/src/superlocalmemory/encoding/scene_builder.py +183 -0
  115. package/src/superlocalmemory/encoding/signal_inference.py +90 -0
  116. package/src/superlocalmemory/encoding/temporal_parser.py +426 -0
  117. package/src/superlocalmemory/encoding/type_router.py +235 -0
  118. package/src/superlocalmemory/hooks/__init__.py +3 -0
  119. package/src/superlocalmemory/hooks/auto_capture.py +111 -0
  120. package/src/superlocalmemory/hooks/auto_recall.py +93 -0
  121. package/src/superlocalmemory/hooks/ide_connector.py +204 -0
  122. package/src/superlocalmemory/hooks/rules_engine.py +99 -0
  123. package/src/superlocalmemory/infra/__init__.py +3 -0
  124. package/src/superlocalmemory/infra/auth_middleware.py +82 -0
  125. package/src/superlocalmemory/infra/backup.py +317 -0
  126. package/src/superlocalmemory/infra/cache_manager.py +267 -0
  127. package/src/superlocalmemory/infra/event_bus.py +381 -0
  128. package/src/superlocalmemory/infra/rate_limiter.py +135 -0
  129. package/src/{webhook_dispatcher.py → superlocalmemory/infra/webhook_dispatcher.py} +104 -101
  130. package/src/superlocalmemory/learning/__init__.py +0 -0
  131. package/src/superlocalmemory/learning/adaptive.py +172 -0
  132. package/src/superlocalmemory/learning/behavioral.py +490 -0
  133. package/src/superlocalmemory/learning/behavioral_listener.py +94 -0
  134. package/src/superlocalmemory/learning/bootstrap.py +298 -0
  135. package/src/superlocalmemory/learning/cross_project.py +399 -0
  136. package/src/superlocalmemory/learning/database.py +376 -0
  137. package/src/superlocalmemory/learning/engagement.py +323 -0
  138. package/src/superlocalmemory/learning/features.py +138 -0
  139. package/src/superlocalmemory/learning/feedback.py +316 -0
  140. package/src/superlocalmemory/learning/outcomes.py +255 -0
  141. package/src/superlocalmemory/learning/project_context.py +366 -0
  142. package/src/superlocalmemory/learning/ranker.py +155 -0
  143. package/src/superlocalmemory/learning/source_quality.py +303 -0
  144. package/src/superlocalmemory/learning/workflows.py +309 -0
  145. package/src/superlocalmemory/llm/__init__.py +0 -0
  146. package/src/superlocalmemory/llm/backbone.py +316 -0
  147. package/src/superlocalmemory/math/__init__.py +0 -0
  148. package/src/superlocalmemory/math/fisher.py +356 -0
  149. package/src/superlocalmemory/math/langevin.py +398 -0
  150. package/src/superlocalmemory/math/sheaf.py +257 -0
  151. package/src/superlocalmemory/mcp/__init__.py +0 -0
  152. package/src/superlocalmemory/mcp/resources.py +245 -0
  153. package/src/superlocalmemory/mcp/server.py +61 -0
  154. package/src/superlocalmemory/mcp/tools.py +18 -0
  155. package/src/superlocalmemory/mcp/tools_core.py +305 -0
  156. package/src/superlocalmemory/mcp/tools_v28.py +223 -0
  157. package/src/superlocalmemory/mcp/tools_v3.py +286 -0
  158. package/src/superlocalmemory/retrieval/__init__.py +0 -0
  159. package/src/superlocalmemory/retrieval/agentic.py +295 -0
  160. package/src/superlocalmemory/retrieval/ann_index.py +223 -0
  161. package/src/superlocalmemory/retrieval/bm25_channel.py +185 -0
  162. package/src/superlocalmemory/retrieval/bridge_discovery.py +170 -0
  163. package/src/superlocalmemory/retrieval/engine.py +390 -0
  164. package/src/superlocalmemory/retrieval/entity_channel.py +179 -0
  165. package/src/superlocalmemory/retrieval/fusion.py +78 -0
  166. package/src/superlocalmemory/retrieval/profile_channel.py +105 -0
  167. package/src/superlocalmemory/retrieval/reranker.py +154 -0
  168. package/src/superlocalmemory/retrieval/semantic_channel.py +232 -0
  169. package/src/superlocalmemory/retrieval/strategy.py +96 -0
  170. package/src/superlocalmemory/retrieval/temporal_channel.py +175 -0
  171. package/src/superlocalmemory/server/__init__.py +1 -0
  172. package/src/superlocalmemory/server/api.py +248 -0
  173. package/src/superlocalmemory/server/routes/__init__.py +4 -0
  174. package/src/superlocalmemory/server/routes/agents.py +107 -0
  175. package/src/superlocalmemory/server/routes/backup.py +91 -0
  176. package/src/superlocalmemory/server/routes/behavioral.py +127 -0
  177. package/src/superlocalmemory/server/routes/compliance.py +160 -0
  178. package/src/superlocalmemory/server/routes/data_io.py +188 -0
  179. package/src/superlocalmemory/server/routes/events.py +183 -0
  180. package/src/superlocalmemory/server/routes/helpers.py +85 -0
  181. package/src/superlocalmemory/server/routes/learning.py +273 -0
  182. package/src/superlocalmemory/server/routes/lifecycle.py +116 -0
  183. package/src/superlocalmemory/server/routes/memories.py +399 -0
  184. package/src/superlocalmemory/server/routes/profiles.py +219 -0
  185. package/src/superlocalmemory/server/routes/stats.py +346 -0
  186. package/src/superlocalmemory/server/routes/v3_api.py +365 -0
  187. package/src/superlocalmemory/server/routes/ws.py +82 -0
  188. package/src/superlocalmemory/server/security_middleware.py +57 -0
  189. package/src/superlocalmemory/server/ui.py +245 -0
  190. package/src/superlocalmemory/storage/__init__.py +0 -0
  191. package/src/superlocalmemory/storage/access_control.py +182 -0
  192. package/src/superlocalmemory/storage/database.py +594 -0
  193. package/src/superlocalmemory/storage/migrations.py +303 -0
  194. package/src/superlocalmemory/storage/models.py +406 -0
  195. package/src/superlocalmemory/storage/schema.py +726 -0
  196. package/src/superlocalmemory/storage/v2_migrator.py +317 -0
  197. package/src/superlocalmemory/trust/__init__.py +0 -0
  198. package/src/superlocalmemory/trust/gate.py +130 -0
  199. package/src/superlocalmemory/trust/provenance.py +124 -0
  200. package/src/superlocalmemory/trust/scorer.py +347 -0
  201. package/src/superlocalmemory/trust/signals.py +153 -0
  202. package/ui/index.html +278 -5
  203. package/ui/js/auto-settings.js +70 -0
  204. package/ui/js/dashboard.js +90 -0
  205. package/ui/js/fact-detail.js +92 -0
  206. package/ui/js/feedback.js +2 -2
  207. package/ui/js/ide-status.js +102 -0
  208. package/ui/js/math-health.js +98 -0
  209. package/ui/js/recall-lab.js +127 -0
  210. package/ui/js/settings.js +2 -2
  211. package/ui/js/trust-dashboard.js +73 -0
  212. package/api_server.py +0 -724
  213. package/bin/aider-smart +0 -72
  214. package/bin/superlocalmemoryv2-learning +0 -4
  215. package/bin/superlocalmemoryv2-list +0 -3
  216. package/bin/superlocalmemoryv2-patterns +0 -4
  217. package/bin/superlocalmemoryv2-profile +0 -3
  218. package/bin/superlocalmemoryv2-recall +0 -3
  219. package/bin/superlocalmemoryv2-remember +0 -3
  220. package/bin/superlocalmemoryv2-reset +0 -3
  221. package/bin/superlocalmemoryv2-status +0 -3
  222. package/configs/chatgpt-desktop-mcp.json +0 -16
  223. package/configs/cursor-mcp.json +0 -15
  224. package/hooks/memory-list-skill.js +0 -139
  225. package/hooks/memory-profile-skill.js +0 -273
  226. package/hooks/memory-recall-skill.js +0 -114
  227. package/hooks/memory-remember-skill.js +0 -127
  228. package/hooks/memory-reset-skill.js +0 -274
  229. package/mcp_server.py +0 -1808
  230. package/requirements-core.txt +0 -22
  231. package/requirements-learning.txt +0 -12
  232. package/requirements.txt +0 -12
  233. package/src/agent_registry.py +0 -411
  234. package/src/auth_middleware.py +0 -61
  235. package/src/auto_backup.py +0 -459
  236. package/src/behavioral/__init__.py +0 -49
  237. package/src/behavioral/behavioral_listener.py +0 -203
  238. package/src/behavioral/behavioral_patterns.py +0 -275
  239. package/src/behavioral/cross_project_transfer.py +0 -206
  240. package/src/behavioral/outcome_inference.py +0 -194
  241. package/src/behavioral/outcome_tracker.py +0 -193
  242. package/src/behavioral/tests/__init__.py +0 -4
  243. package/src/behavioral/tests/test_behavioral_integration.py +0 -108
  244. package/src/behavioral/tests/test_behavioral_patterns.py +0 -150
  245. package/src/behavioral/tests/test_cross_project_transfer.py +0 -142
  246. package/src/behavioral/tests/test_mcp_behavioral.py +0 -139
  247. package/src/behavioral/tests/test_mcp_report_outcome.py +0 -117
  248. package/src/behavioral/tests/test_outcome_inference.py +0 -107
  249. package/src/behavioral/tests/test_outcome_tracker.py +0 -96
  250. package/src/cache_manager.py +0 -518
  251. package/src/compliance/__init__.py +0 -48
  252. package/src/compliance/abac_engine.py +0 -149
  253. package/src/compliance/abac_middleware.py +0 -116
  254. package/src/compliance/audit_db.py +0 -215
  255. package/src/compliance/audit_logger.py +0 -148
  256. package/src/compliance/retention_manager.py +0 -289
  257. package/src/compliance/retention_scheduler.py +0 -186
  258. package/src/compliance/tests/__init__.py +0 -4
  259. package/src/compliance/tests/test_abac_enforcement.py +0 -95
  260. package/src/compliance/tests/test_abac_engine.py +0 -124
  261. package/src/compliance/tests/test_abac_mcp_integration.py +0 -118
  262. package/src/compliance/tests/test_audit_db.py +0 -123
  263. package/src/compliance/tests/test_audit_logger.py +0 -98
  264. package/src/compliance/tests/test_mcp_audit.py +0 -128
  265. package/src/compliance/tests/test_mcp_retention_policy.py +0 -125
  266. package/src/compliance/tests/test_retention_manager.py +0 -131
  267. package/src/compliance/tests/test_retention_scheduler.py +0 -99
  268. package/src/compression/__init__.py +0 -25
  269. package/src/compression/cli.py +0 -150
  270. package/src/compression/cold_storage.py +0 -217
  271. package/src/compression/config.py +0 -72
  272. package/src/compression/orchestrator.py +0 -133
  273. package/src/compression/tier2_compressor.py +0 -228
  274. package/src/compression/tier3_compressor.py +0 -153
  275. package/src/compression/tier_classifier.py +0 -148
  276. package/src/db_connection_manager.py +0 -536
  277. package/src/embedding_engine.py +0 -63
  278. package/src/embeddings/__init__.py +0 -47
  279. package/src/embeddings/cache.py +0 -70
  280. package/src/embeddings/cli.py +0 -113
  281. package/src/embeddings/constants.py +0 -47
  282. package/src/embeddings/database.py +0 -91
  283. package/src/embeddings/engine.py +0 -247
  284. package/src/embeddings/model_loader.py +0 -145
  285. package/src/event_bus.py +0 -562
  286. package/src/graph/__init__.py +0 -36
  287. package/src/graph/build_helpers.py +0 -74
  288. package/src/graph/cli.py +0 -87
  289. package/src/graph/cluster_builder.py +0 -188
  290. package/src/graph/cluster_summary.py +0 -148
  291. package/src/graph/constants.py +0 -47
  292. package/src/graph/edge_builder.py +0 -162
  293. package/src/graph/entity_extractor.py +0 -95
  294. package/src/graph/graph_core.py +0 -226
  295. package/src/graph/graph_search.py +0 -231
  296. package/src/graph/hierarchical.py +0 -207
  297. package/src/graph/schema.py +0 -99
  298. package/src/graph_engine.py +0 -52
  299. package/src/hnsw_index.py +0 -628
  300. package/src/hybrid_search.py +0 -46
  301. package/src/learning/__init__.py +0 -217
  302. package/src/learning/adaptive_ranker.py +0 -682
  303. package/src/learning/bootstrap/__init__.py +0 -69
  304. package/src/learning/bootstrap/constants.py +0 -93
  305. package/src/learning/bootstrap/db_queries.py +0 -316
  306. package/src/learning/bootstrap/sampling.py +0 -82
  307. package/src/learning/bootstrap/text_utils.py +0 -71
  308. package/src/learning/cross_project_aggregator.py +0 -857
  309. package/src/learning/db/__init__.py +0 -40
  310. package/src/learning/db/constants.py +0 -44
  311. package/src/learning/db/schema.py +0 -279
  312. package/src/learning/engagement_tracker.py +0 -628
  313. package/src/learning/feature_extractor.py +0 -708
  314. package/src/learning/feedback_collector.py +0 -806
  315. package/src/learning/learning_db.py +0 -915
  316. package/src/learning/project_context_manager.py +0 -572
  317. package/src/learning/ranking/__init__.py +0 -33
  318. package/src/learning/ranking/constants.py +0 -84
  319. package/src/learning/ranking/helpers.py +0 -278
  320. package/src/learning/source_quality_scorer.py +0 -676
  321. package/src/learning/synthetic_bootstrap.py +0 -755
  322. package/src/learning/tests/test_adaptive_ranker.py +0 -325
  323. package/src/learning/tests/test_adaptive_ranker_v28.py +0 -60
  324. package/src/learning/tests/test_aggregator.py +0 -306
  325. package/src/learning/tests/test_auto_retrain_v28.py +0 -35
  326. package/src/learning/tests/test_e2e_ranking_v28.py +0 -82
  327. package/src/learning/tests/test_feature_extractor_v28.py +0 -93
  328. package/src/learning/tests/test_feedback_collector.py +0 -294
  329. package/src/learning/tests/test_learning_db.py +0 -602
  330. package/src/learning/tests/test_learning_db_v28.py +0 -110
  331. package/src/learning/tests/test_learning_init_v28.py +0 -48
  332. package/src/learning/tests/test_outcome_signals.py +0 -48
  333. package/src/learning/tests/test_project_context.py +0 -292
  334. package/src/learning/tests/test_schema_migration.py +0 -319
  335. package/src/learning/tests/test_signal_inference.py +0 -397
  336. package/src/learning/tests/test_source_quality.py +0 -351
  337. package/src/learning/tests/test_synthetic_bootstrap.py +0 -429
  338. package/src/learning/tests/test_workflow_miner.py +0 -318
  339. package/src/learning/workflow_pattern_miner.py +0 -655
  340. package/src/lifecycle/__init__.py +0 -54
  341. package/src/lifecycle/bounded_growth.py +0 -239
  342. package/src/lifecycle/compaction_engine.py +0 -226
  343. package/src/lifecycle/lifecycle_engine.py +0 -355
  344. package/src/lifecycle/lifecycle_evaluator.py +0 -257
  345. package/src/lifecycle/lifecycle_scheduler.py +0 -130
  346. package/src/lifecycle/retention_policy.py +0 -285
  347. package/src/lifecycle/tests/test_bounded_growth.py +0 -193
  348. package/src/lifecycle/tests/test_compaction.py +0 -179
  349. package/src/lifecycle/tests/test_lifecycle_engine.py +0 -137
  350. package/src/lifecycle/tests/test_lifecycle_evaluation.py +0 -177
  351. package/src/lifecycle/tests/test_lifecycle_scheduler.py +0 -127
  352. package/src/lifecycle/tests/test_lifecycle_search.py +0 -109
  353. package/src/lifecycle/tests/test_mcp_compact.py +0 -149
  354. package/src/lifecycle/tests/test_mcp_lifecycle_status.py +0 -114
  355. package/src/lifecycle/tests/test_retention_policy.py +0 -162
  356. package/src/mcp_tools_v28.py +0 -281
  357. package/src/memory/__init__.py +0 -36
  358. package/src/memory/cli.py +0 -205
  359. package/src/memory/constants.py +0 -39
  360. package/src/memory/helpers.py +0 -28
  361. package/src/memory/schema.py +0 -166
  362. package/src/memory-profiles.py +0 -595
  363. package/src/memory-reset.py +0 -491
  364. package/src/memory_compression.py +0 -989
  365. package/src/memory_store_v2.py +0 -1155
  366. package/src/migrate_v1_to_v2.py +0 -629
  367. package/src/pattern_learner.py +0 -34
  368. package/src/patterns/__init__.py +0 -24
  369. package/src/patterns/analyzers.py +0 -251
  370. package/src/patterns/learner.py +0 -271
  371. package/src/patterns/scoring.py +0 -171
  372. package/src/patterns/store.py +0 -225
  373. package/src/patterns/terminology.py +0 -140
  374. package/src/provenance_tracker.py +0 -312
  375. package/src/qualixar_attribution.py +0 -139
  376. package/src/qualixar_watermark.py +0 -78
  377. package/src/query_optimizer.py +0 -511
  378. package/src/rate_limiter.py +0 -83
  379. package/src/search/__init__.py +0 -20
  380. package/src/search/cli.py +0 -77
  381. package/src/search/constants.py +0 -26
  382. package/src/search/engine.py +0 -241
  383. package/src/search/fusion.py +0 -122
  384. package/src/search/index_loader.py +0 -114
  385. package/src/search/methods.py +0 -162
  386. package/src/search_engine_v2.py +0 -401
  387. package/src/setup_validator.py +0 -482
  388. package/src/subscription_manager.py +0 -391
  389. package/src/tree/__init__.py +0 -59
  390. package/src/tree/builder.py +0 -185
  391. package/src/tree/nodes.py +0 -202
  392. package/src/tree/queries.py +0 -257
  393. package/src/tree/schema.py +0 -80
  394. package/src/tree_manager.py +0 -19
  395. package/src/trust/__init__.py +0 -45
  396. package/src/trust/constants.py +0 -66
  397. package/src/trust/queries.py +0 -157
  398. package/src/trust/schema.py +0 -95
  399. package/src/trust/scorer.py +0 -299
  400. package/src/trust/signals.py +0 -95
  401. package/src/trust_scorer.py +0 -44
  402. package/ui/app.js +0 -1588
  403. package/ui/js/graph-cytoscape-monolithic-backup.js +0 -1168
  404. package/ui/js/graph-cytoscape.js +0 -1168
  405. package/ui/js/graph-d3-backup.js +0 -32
  406. package/ui/js/graph.js +0 -32
  407. package/ui_server.py +0 -286
  408. /package/docs/{ACCESSIBILITY.md → v2-archive/ACCESSIBILITY.md} +0 -0
  409. /package/docs/{ARCHITECTURE.md → v2-archive/ARCHITECTURE.md} +0 -0
  410. /package/docs/{CLI-COMMANDS-REFERENCE.md → v2-archive/CLI-COMMANDS-REFERENCE.md} +0 -0
  411. /package/docs/{COMPRESSION-README.md → v2-archive/COMPRESSION-README.md} +0 -0
  412. /package/docs/{FRAMEWORK-INTEGRATIONS.md → v2-archive/FRAMEWORK-INTEGRATIONS.md} +0 -0
  413. /package/docs/{MCP-MANUAL-SETUP.md → v2-archive/MCP-MANUAL-SETUP.md} +0 -0
  414. /package/docs/{MCP-TROUBLESHOOTING.md → v2-archive/MCP-TROUBLESHOOTING.md} +0 -0
  415. /package/docs/{PATTERN-LEARNING.md → v2-archive/PATTERN-LEARNING.md} +0 -0
  416. /package/docs/{PROFILES-GUIDE.md → v2-archive/PROFILES-GUIDE.md} +0 -0
  417. /package/docs/{RESET-GUIDE.md → v2-archive/RESET-GUIDE.md} +0 -0
  418. /package/docs/{SEARCH-ENGINE-V2.2.0.md → v2-archive/SEARCH-ENGINE-V2.2.0.md} +0 -0
  419. /package/docs/{SEARCH-INTEGRATION-GUIDE.md → v2-archive/SEARCH-INTEGRATION-GUIDE.md} +0 -0
  420. /package/docs/{UI-SERVER.md → v2-archive/UI-SERVER.md} +0 -0
  421. /package/docs/{UNIVERSAL-INTEGRATION.md → v2-archive/UNIVERSAL-INTEGRATION.md} +0 -0
  422. /package/docs/{V2.2.0-OPTIONAL-SEARCH.md → v2-archive/V2.2.0-OPTIONAL-SEARCH.md} +0 -0
  423. /package/docs/{WINDOWS-INSTALL-README.txt → v2-archive/WINDOWS-INSTALL-README.txt} +0 -0
  424. /package/docs/{WINDOWS-POST-INSTALL.txt → v2-archive/WINDOWS-POST-INSTALL.txt} +0 -0
  425. /package/docs/{example_graph_usage.py → v2-archive/example_graph_usage.py} +0 -0
  426. /package/{completions → ide/completions}/slm.bash +0 -0
  427. /package/{completions → ide/completions}/slm.zsh +0 -0
  428. /package/{configs → ide/configs}/cody-commands.json +0 -0
  429. /package/{install-skills.sh → scripts/install-skills.sh} +0 -0
  430. /package/{install.ps1 → scripts/install.ps1} +0 -0
  431. /package/{install.sh → scripts/install.sh} +0 -0
@@ -1,124 +0,0 @@
1
- # SPDX-License-Identifier: MIT
2
- # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
- """Tests for ABAC policy engine.
4
- """
5
- import tempfile
6
- import os
7
- import sys
8
- import json
9
- from pathlib import Path
10
-
11
- sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
12
-
13
-
14
- class TestABACEngine:
15
- def setup_method(self):
16
- self.tmp_dir = tempfile.mkdtemp()
17
- self.policy_path = os.path.join(self.tmp_dir, "abac_policies.json")
18
-
19
- def teardown_method(self):
20
- import shutil
21
- shutil.rmtree(self.tmp_dir, ignore_errors=True)
22
-
23
- def _write_policies(self, policies):
24
- with open(self.policy_path, "w") as f:
25
- json.dump(policies, f)
26
-
27
- def test_creation_no_policy_file(self):
28
- """Engine works with no policy file — allow all."""
29
- from compliance.abac_engine import ABACEngine
30
- engine = ABACEngine(config_path="/nonexistent/path.json")
31
- assert engine is not None
32
-
33
- def test_missing_policy_allows_all(self):
34
- """No policy file → all access allowed (backward compat)."""
35
- from compliance.abac_engine import ABACEngine
36
- engine = ABACEngine(config_path="/nonexistent/path.json")
37
- result = engine.evaluate(subject={"agent_id": "user"}, resource={"access_level": "public"}, action="read")
38
- assert result["allowed"] is True
39
-
40
- def test_load_policies_from_json(self):
41
- """Can load policies from JSON file."""
42
- from compliance.abac_engine import ABACEngine
43
- self._write_policies([
44
- {"name": "deny-private", "effect": "deny", "subjects": {"agent_id": "*"}, "resources": {"access_level": "private"}, "actions": ["read"]}
45
- ])
46
- engine = ABACEngine(config_path=self.policy_path)
47
- assert len(engine.policies) == 1
48
-
49
- def test_deny_policy_blocks_access(self):
50
- """Deny policy prevents access to matching resources."""
51
- from compliance.abac_engine import ABACEngine
52
- self._write_policies([
53
- {"name": "deny-private", "effect": "deny", "subjects": {"agent_id": "*"}, "resources": {"access_level": "private"}, "actions": ["read"]}
54
- ])
55
- engine = ABACEngine(config_path=self.policy_path)
56
- result = engine.evaluate(subject={"agent_id": "agent_a"}, resource={"access_level": "private"}, action="read")
57
- assert result["allowed"] is False
58
- assert result["policy_name"] == "deny-private"
59
-
60
- def test_allow_policy_grants_access(self):
61
- """Allow policy explicitly permits access."""
62
- from compliance.abac_engine import ABACEngine
63
- self._write_policies([
64
- {"name": "allow-admin", "effect": "allow", "subjects": {"agent_id": "admin"}, "resources": {"access_level": "*"}, "actions": ["read", "write", "delete"]}
65
- ])
66
- engine = ABACEngine(config_path=self.policy_path)
67
- result = engine.evaluate(subject={"agent_id": "admin"}, resource={"access_level": "private"}, action="write")
68
- assert result["allowed"] is True
69
-
70
- def test_subject_matching_specific_agent(self):
71
- """Policy matches specific agent_id."""
72
- from compliance.abac_engine import ABACEngine
73
- self._write_policies([
74
- {"name": "deny-untrusted", "effect": "deny", "subjects": {"agent_id": "untrusted_bot"}, "resources": {"access_level": "*"}, "actions": ["read"]}
75
- ])
76
- engine = ABACEngine(config_path=self.policy_path)
77
- # untrusted_bot denied
78
- r1 = engine.evaluate(subject={"agent_id": "untrusted_bot"}, resource={"access_level": "public"}, action="read")
79
- assert r1["allowed"] is False
80
- # trusted_agent allowed (no matching deny policy)
81
- r2 = engine.evaluate(subject={"agent_id": "trusted_agent"}, resource={"access_level": "public"}, action="read")
82
- assert r2["allowed"] is True
83
-
84
- def test_resource_matching_by_project(self):
85
- """Policy matches by project name."""
86
- from compliance.abac_engine import ABACEngine
87
- self._write_policies([
88
- {"name": "deny-secret-project", "effect": "deny", "subjects": {"agent_id": "*"}, "resources": {"project": "secret_project"}, "actions": ["read"]}
89
- ])
90
- engine = ABACEngine(config_path=self.policy_path)
91
- r1 = engine.evaluate(subject={"agent_id": "user"}, resource={"project": "secret_project"}, action="read")
92
- assert r1["allowed"] is False
93
- r2 = engine.evaluate(subject={"agent_id": "user"}, resource={"project": "public_project"}, action="read")
94
- assert r2["allowed"] is True
95
-
96
- def test_action_matching(self):
97
- """Policy only applies to specified actions."""
98
- from compliance.abac_engine import ABACEngine
99
- self._write_policies([
100
- {"name": "deny-delete", "effect": "deny", "subjects": {"agent_id": "*"}, "resources": {"access_level": "*"}, "actions": ["delete"]}
101
- ])
102
- engine = ABACEngine(config_path=self.policy_path)
103
- r1 = engine.evaluate(subject={"agent_id": "user"}, resource={"access_level": "public"}, action="delete")
104
- assert r1["allowed"] is False
105
- r2 = engine.evaluate(subject={"agent_id": "user"}, resource={"access_level": "public"}, action="read")
106
- assert r2["allowed"] is True
107
-
108
- def test_deny_takes_precedence(self):
109
- """When both allow and deny match, deny wins."""
110
- from compliance.abac_engine import ABACEngine
111
- self._write_policies([
112
- {"name": "allow-all", "effect": "allow", "subjects": {"agent_id": "*"}, "resources": {"access_level": "*"}, "actions": ["read"]},
113
- {"name": "deny-private", "effect": "deny", "subjects": {"agent_id": "*"}, "resources": {"access_level": "private"}, "actions": ["read"]}
114
- ])
115
- engine = ABACEngine(config_path=self.policy_path)
116
- result = engine.evaluate(subject={"agent_id": "user"}, resource={"access_level": "private"}, action="read")
117
- assert result["allowed"] is False
118
-
119
- def test_evaluate_returns_reason(self):
120
- """Evaluation result includes reason."""
121
- from compliance.abac_engine import ABACEngine
122
- engine = ABACEngine(config_path="/nonexistent/path.json")
123
- result = engine.evaluate(subject={"agent_id": "user"}, resource={}, action="read")
124
- assert "reason" in result
@@ -1,118 +0,0 @@
1
- # SPDX-License-Identifier: MIT
2
- # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
- """Tests for ABAC enforcement via MCP tool integration.
4
- """
5
- import tempfile
6
- import os
7
- import sys
8
- import json
9
- from pathlib import Path
10
-
11
- sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
12
-
13
-
14
- class TestABACMCPIntegration:
15
- def setup_method(self):
16
- self.tmp_dir = tempfile.mkdtemp()
17
- self.db_path = os.path.join(self.tmp_dir, "memory.db")
18
-
19
- def teardown_method(self):
20
- import shutil
21
-
22
- shutil.rmtree(self.tmp_dir, ignore_errors=True)
23
-
24
- def test_middleware_creation(self):
25
- from compliance.abac_middleware import ABACMiddleware
26
-
27
- mw = ABACMiddleware(self.db_path)
28
- assert mw is not None
29
-
30
- def test_check_read_access_default_allow(self):
31
- """Default (no policies) allows all reads."""
32
- from compliance.abac_middleware import ABACMiddleware
33
-
34
- mw = ABACMiddleware(self.db_path)
35
- result = mw.check_access(
36
- agent_id="any_agent",
37
- action="read",
38
- resource={"access_level": "public"},
39
- )
40
- assert result["allowed"] is True
41
-
42
- def test_check_write_access_default_allow(self):
43
- from compliance.abac_middleware import ABACMiddleware
44
-
45
- mw = ABACMiddleware(self.db_path)
46
- result = mw.check_access(
47
- agent_id="any_agent", action="write", resource={}
48
- )
49
- assert result["allowed"] is True
50
-
51
- def test_check_access_with_deny_policy(self):
52
- """Deny policy blocks access when enforced."""
53
- from compliance.abac_middleware import ABACMiddleware
54
-
55
- policy_path = os.path.join(self.tmp_dir, "abac_policies.json")
56
- with open(policy_path, "w") as f:
57
- json.dump(
58
- [
59
- {
60
- "name": "deny-bots",
61
- "effect": "deny",
62
- "subjects": {"agent_id": "untrusted_bot"},
63
- "resources": {"access_level": "*"},
64
- "actions": ["read", "write"],
65
- }
66
- ],
67
- f,
68
- )
69
- mw = ABACMiddleware(self.db_path, policy_path=policy_path)
70
- result = mw.check_access(
71
- agent_id="untrusted_bot",
72
- action="read",
73
- resource={"access_level": "public"},
74
- )
75
- assert result["allowed"] is False
76
-
77
- def test_denied_access_logged(self):
78
- """Denied access is recorded for audit trail."""
79
- from compliance.abac_middleware import ABACMiddleware
80
-
81
- policy_path = os.path.join(self.tmp_dir, "abac_policies.json")
82
- with open(policy_path, "w") as f:
83
- json.dump(
84
- [
85
- {
86
- "name": "deny-all-write",
87
- "effect": "deny",
88
- "subjects": {"agent_id": "*"},
89
- "resources": {"access_level": "*"},
90
- "actions": ["write"],
91
- }
92
- ],
93
- f,
94
- )
95
- mw = ABACMiddleware(self.db_path, policy_path=policy_path)
96
- mw.check_access(agent_id="user", action="write", resource={})
97
- assert mw.denied_count >= 1
98
-
99
- def test_build_agent_context(self):
100
- """build_agent_context creates proper context dict for store."""
101
- from compliance.abac_middleware import ABACMiddleware
102
-
103
- mw = ABACMiddleware(self.db_path)
104
- ctx = mw.build_agent_context(agent_id="claude_agent", protocol="mcp")
105
- assert ctx["agent_id"] == "claude_agent"
106
- assert ctx["protocol"] == "mcp"
107
-
108
- def test_graceful_when_compliance_unavailable(self):
109
- """Middleware works even if ABACEngine import fails."""
110
- from compliance.abac_middleware import ABACMiddleware
111
-
112
- mw = ABACMiddleware(
113
- self.db_path, policy_path="/nonexistent/path.json"
114
- )
115
- result = mw.check_access(
116
- agent_id="user", action="read", resource={}
117
- )
118
- assert result["allowed"] is True
@@ -1,123 +0,0 @@
1
- # SPDX-License-Identifier: MIT
2
- # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
- """Tests for audit database with hash chain tamper detection.
4
- """
5
- import sqlite3
6
- import tempfile
7
- import os
8
- import sys
9
- import hashlib
10
- import json
11
- from pathlib import Path
12
-
13
- sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
14
-
15
-
16
- class TestAuditDB:
17
- def setup_method(self):
18
- self.tmp_dir = tempfile.mkdtemp()
19
- self.db_path = os.path.join(self.tmp_dir, "audit.db")
20
-
21
- def teardown_method(self):
22
- import shutil
23
- shutil.rmtree(self.tmp_dir, ignore_errors=True)
24
-
25
- def test_creation(self):
26
- from compliance.audit_db import AuditDB
27
- db = AuditDB(self.db_path)
28
- assert db is not None
29
-
30
- def test_schema_created(self):
31
- from compliance.audit_db import AuditDB
32
- db = AuditDB(self.db_path)
33
- conn = sqlite3.connect(self.db_path)
34
- tables = {r[0] for r in conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()}
35
- conn.close()
36
- assert "audit_events" in tables
37
-
38
- def test_log_event(self):
39
- from compliance.audit_db import AuditDB
40
- db = AuditDB(self.db_path)
41
- eid = db.log_event(event_type="memory.created", actor="user", resource_id=1, details={"action": "create"})
42
- assert isinstance(eid, int)
43
- assert eid > 0
44
-
45
- def test_hash_chain_first_entry(self):
46
- """First entry's prev_hash should be a known genesis value."""
47
- from compliance.audit_db import AuditDB
48
- db = AuditDB(self.db_path)
49
- db.log_event("memory.created", actor="user", resource_id=1)
50
- conn = sqlite3.connect(self.db_path)
51
- row = conn.execute("SELECT prev_hash, entry_hash FROM audit_events WHERE id=1").fetchone()
52
- conn.close()
53
- assert row[0] == "genesis"
54
- assert row[1] is not None and len(row[1]) == 64 # SHA-256 hex
55
-
56
- def test_hash_chain_links(self):
57
- """Each entry's prev_hash should equal the previous entry's entry_hash."""
58
- from compliance.audit_db import AuditDB
59
- db = AuditDB(self.db_path)
60
- db.log_event("memory.created", actor="user", resource_id=1)
61
- db.log_event("memory.recalled", actor="agent_a", resource_id=2)
62
- db.log_event("memory.deleted", actor="user", resource_id=1)
63
- conn = sqlite3.connect(self.db_path)
64
- rows = conn.execute("SELECT id, prev_hash, entry_hash FROM audit_events ORDER BY id").fetchall()
65
- conn.close()
66
- assert rows[1][1] == rows[0][2] # Entry 2's prev = Entry 1's hash
67
- assert rows[2][1] == rows[1][2] # Entry 3's prev = Entry 2's hash
68
-
69
- def test_verify_chain_valid(self):
70
- """verify_chain returns True for untampered chain."""
71
- from compliance.audit_db import AuditDB
72
- db = AuditDB(self.db_path)
73
- db.log_event("memory.created", actor="user", resource_id=1)
74
- db.log_event("memory.recalled", actor="agent_a", resource_id=1)
75
- result = db.verify_chain()
76
- assert result["valid"] is True
77
- assert result["entries_checked"] == 2
78
-
79
- def test_verify_chain_detects_tampering(self):
80
- """verify_chain returns False if an entry was modified."""
81
- from compliance.audit_db import AuditDB
82
- db = AuditDB(self.db_path)
83
- db.log_event("memory.created", actor="user", resource_id=1)
84
- db.log_event("memory.recalled", actor="agent_a", resource_id=1)
85
- # Tamper with the first entry
86
- conn = sqlite3.connect(self.db_path)
87
- conn.execute("UPDATE audit_events SET actor='hacker' WHERE id=1")
88
- conn.commit()
89
- conn.close()
90
- result = db.verify_chain()
91
- assert result["valid"] is False
92
-
93
- def test_query_by_type(self):
94
- from compliance.audit_db import AuditDB
95
- db = AuditDB(self.db_path)
96
- db.log_event("memory.created", actor="user", resource_id=1)
97
- db.log_event("memory.recalled", actor="user", resource_id=1)
98
- db.log_event("memory.created", actor="user", resource_id=2)
99
- results = db.query_events(event_type="memory.created")
100
- assert len(results) == 2
101
-
102
- def test_query_by_actor(self):
103
- from compliance.audit_db import AuditDB
104
- db = AuditDB(self.db_path)
105
- db.log_event("memory.created", actor="user", resource_id=1)
106
- db.log_event("memory.recalled", actor="agent_a", resource_id=1)
107
- results = db.query_events(actor="agent_a")
108
- assert len(results) == 1
109
-
110
- def test_query_by_time_range(self):
111
- from compliance.audit_db import AuditDB
112
- db = AuditDB(self.db_path)
113
- db.log_event("memory.created", actor="user", resource_id=1)
114
- results = db.query_events(limit=10)
115
- assert len(results) >= 1
116
- assert "created_at" in results[0]
117
-
118
- def test_empty_chain_is_valid(self):
119
- from compliance.audit_db import AuditDB
120
- db = AuditDB(self.db_path)
121
- result = db.verify_chain()
122
- assert result["valid"] is True
123
- assert result["entries_checked"] == 0
@@ -1,98 +0,0 @@
1
- # SPDX-License-Identifier: MIT
2
- # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
- """Tests for audit logger EventBus listener.
4
- """
5
- import tempfile, os, sys, sqlite3
6
- from datetime import datetime
7
- from pathlib import Path
8
- sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
9
-
10
- class TestAuditLogger:
11
- def setup_method(self):
12
- self.tmp_dir = tempfile.mkdtemp()
13
- self.audit_db_path = os.path.join(self.tmp_dir, "audit.db")
14
-
15
- def teardown_method(self):
16
- import shutil
17
- shutil.rmtree(self.tmp_dir, ignore_errors=True)
18
-
19
- def test_creation(self):
20
- from compliance.audit_logger import AuditLogger
21
- logger = AuditLogger(self.audit_db_path)
22
- assert logger is not None
23
-
24
- def test_logs_memory_created(self):
25
- from compliance.audit_logger import AuditLogger
26
- logger = AuditLogger(self.audit_db_path)
27
- logger.handle_event({"event_type": "memory.created", "memory_id": 1, "payload": {}, "timestamp": datetime.now().isoformat(), "source_agent": "user"})
28
- conn = sqlite3.connect(self.audit_db_path)
29
- rows = conn.execute("SELECT * FROM audit_events WHERE event_type='memory.created'").fetchall()
30
- conn.close()
31
- assert len(rows) == 1
32
-
33
- def test_logs_memory_recalled(self):
34
- from compliance.audit_logger import AuditLogger
35
- logger = AuditLogger(self.audit_db_path)
36
- logger.handle_event({"event_type": "memory.recalled", "memory_id": 2, "payload": {"query": "test"}, "timestamp": datetime.now().isoformat(), "source_agent": "agent_a"})
37
- conn = sqlite3.connect(self.audit_db_path)
38
- rows = conn.execute("SELECT * FROM audit_events WHERE event_type='memory.recalled'").fetchall()
39
- conn.close()
40
- assert len(rows) == 1
41
-
42
- def test_logs_memory_deleted(self):
43
- from compliance.audit_logger import AuditLogger
44
- logger = AuditLogger(self.audit_db_path)
45
- logger.handle_event({"event_type": "memory.deleted", "memory_id": 3, "payload": {}, "timestamp": datetime.now().isoformat(), "source_agent": "user"})
46
- conn = sqlite3.connect(self.audit_db_path)
47
- rows = conn.execute("SELECT * FROM audit_events WHERE event_type='memory.deleted'").fetchall()
48
- conn.close()
49
- assert len(rows) == 1
50
-
51
- def test_hash_chain_maintained(self):
52
- """Multiple events maintain hash chain integrity."""
53
- from compliance.audit_logger import AuditLogger
54
- logger = AuditLogger(self.audit_db_path)
55
- for i in range(5):
56
- logger.handle_event({"event_type": "memory.created", "memory_id": i, "payload": {}, "timestamp": datetime.now().isoformat(), "source_agent": "user"})
57
- from compliance.audit_db import AuditDB
58
- db = AuditDB(self.audit_db_path)
59
- result = db.verify_chain()
60
- assert result["valid"] is True
61
- assert result["entries_checked"] == 5
62
-
63
- def test_logs_lifecycle_transitions(self):
64
- from compliance.audit_logger import AuditLogger
65
- logger = AuditLogger(self.audit_db_path)
66
- logger.handle_event({"event_type": "lifecycle.transitioned", "memory_id": 1, "payload": {"from_state": "active", "to_state": "warm"}, "timestamp": datetime.now().isoformat(), "source_agent": "scheduler"})
67
- conn = sqlite3.connect(self.audit_db_path)
68
- rows = conn.execute("SELECT * FROM audit_events").fetchall()
69
- conn.close()
70
- assert len(rows) == 1
71
-
72
- def test_ignores_unknown_gracefully(self):
73
- """Unknown event types logged without error."""
74
- from compliance.audit_logger import AuditLogger
75
- logger = AuditLogger(self.audit_db_path)
76
- logger.handle_event({"event_type": "unknown.event", "payload": {}, "timestamp": datetime.now().isoformat(), "source_agent": "test"})
77
- assert logger.events_logged >= 1
78
-
79
- def test_graceful_on_malformed_event(self):
80
- """Malformed events don't crash the logger."""
81
- from compliance.audit_logger import AuditLogger
82
- logger = AuditLogger(self.audit_db_path)
83
- logger.handle_event({}) # Empty event
84
- logger.handle_event({"event_type": "test"}) # Missing fields
85
- # Should not crash
86
-
87
- def test_register_with_eventbus(self):
88
- from compliance.audit_logger import AuditLogger
89
- logger = AuditLogger(self.audit_db_path)
90
- result = logger.register_with_eventbus()
91
- assert isinstance(result, bool)
92
-
93
- def test_get_status(self):
94
- from compliance.audit_logger import AuditLogger
95
- logger = AuditLogger(self.audit_db_path)
96
- status = logger.get_status()
97
- assert "events_logged" in status
98
- assert "registered" in status
@@ -1,128 +0,0 @@
1
- # SPDX-License-Identifier: MIT
2
- # Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
- """Tests for audit_trail MCP tool handler.
4
-
5
- Validates the MCP wrapper around AuditDB — tests empty trail, event logging,
6
- event_type and actor filtering, and hash chain verification.
7
- """
8
- import asyncio
9
- import os
10
- import shutil
11
- import sys
12
- import tempfile
13
- from pathlib import Path
14
-
15
- import pytest
16
-
17
- sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent))
18
-
19
-
20
- class TestMCPAuditTrail:
21
- """Tests for the audit_trail tool handler."""
22
-
23
- def setup_method(self):
24
- self.tmp_dir = tempfile.mkdtemp()
25
- self.db_path = os.path.join(self.tmp_dir, "audit.db")
26
-
27
- def teardown_method(self):
28
- shutil.rmtree(self.tmp_dir, ignore_errors=True)
29
-
30
- def _run(self, coro):
31
- return asyncio.get_event_loop().run_until_complete(coro)
32
-
33
- def test_empty_trail(self):
34
- """Fresh audit DB should return count=0."""
35
- import mcp_tools_v28 as tools
36
- tools.DEFAULT_AUDIT_DB = self.db_path
37
-
38
- result = self._run(tools.audit_trail())
39
- assert result["success"] is True
40
- assert result["count"] == 0
41
- assert result["events"] == []
42
-
43
- def test_verify_empty_chain(self):
44
- """Hash chain verification on empty DB should be valid."""
45
- import mcp_tools_v28 as tools
46
- tools.DEFAULT_AUDIT_DB = self.db_path
47
-
48
- result = self._run(tools.audit_trail(verify_chain=True))
49
- assert result["success"] is True
50
- assert result["chain_valid"] is True
51
- assert result["chain_entries"] == 0
52
-
53
- def test_query_with_events(self):
54
- """After logging events, query should return them."""
55
- from compliance.audit_db import AuditDB
56
-
57
- db = AuditDB(self.db_path)
58
- db.log_event("memory.created", actor="user", resource_id=1)
59
- db.log_event("memory.recalled", actor="agent_a", resource_id=1)
60
- db.log_event("memory.created", actor="user", resource_id=2)
61
-
62
- import mcp_tools_v28 as tools
63
- tools.DEFAULT_AUDIT_DB = self.db_path
64
-
65
- result = self._run(tools.audit_trail())
66
- assert result["success"] is True
67
- assert result["count"] == 3
68
-
69
- def test_filter_by_event_type(self):
70
- """Filtering by event_type should narrow results."""
71
- from compliance.audit_db import AuditDB
72
-
73
- db = AuditDB(self.db_path)
74
- db.log_event("memory.created", actor="user", resource_id=1)
75
- db.log_event("memory.recalled", actor="agent_a", resource_id=1)
76
-
77
- import mcp_tools_v28 as tools
78
- tools.DEFAULT_AUDIT_DB = self.db_path
79
-
80
- result = self._run(tools.audit_trail(event_type="memory.created"))
81
- assert result["count"] == 1
82
- assert result["events"][0]["event_type"] == "memory.created"
83
-
84
- def test_filter_by_actor(self):
85
- """Filtering by actor should narrow results."""
86
- from compliance.audit_db import AuditDB
87
-
88
- db = AuditDB(self.db_path)
89
- db.log_event("memory.created", actor="user", resource_id=1)
90
- db.log_event("memory.recalled", actor="agent_a", resource_id=1)
91
-
92
- import mcp_tools_v28 as tools
93
- tools.DEFAULT_AUDIT_DB = self.db_path
94
-
95
- result = self._run(tools.audit_trail(actor="agent_a"))
96
- assert result["count"] == 1
97
- assert result["events"][0]["actor"] == "agent_a"
98
-
99
- def test_verify_chain_with_events(self):
100
- """Hash chain with events should verify successfully."""
101
- from compliance.audit_db import AuditDB
102
-
103
- db = AuditDB(self.db_path)
104
- db.log_event("memory.created", actor="user", resource_id=1)
105
- db.log_event("memory.recalled", actor="user", resource_id=1)
106
- db.log_event("memory.updated", actor="user", resource_id=1)
107
-
108
- import mcp_tools_v28 as tools
109
- tools.DEFAULT_AUDIT_DB = self.db_path
110
-
111
- result = self._run(tools.audit_trail(verify_chain=True))
112
- assert result["success"] is True
113
- assert result["chain_valid"] is True
114
- assert result["chain_entries"] == 3
115
-
116
- def test_limit_parameter(self):
117
- """Limit parameter should cap returned events."""
118
- from compliance.audit_db import AuditDB
119
-
120
- db = AuditDB(self.db_path)
121
- for i in range(10):
122
- db.log_event("memory.created", actor="user", resource_id=i)
123
-
124
- import mcp_tools_v28 as tools
125
- tools.DEFAULT_AUDIT_DB = self.db_path
126
-
127
- result = self._run(tools.audit_trail(limit=3))
128
- assert result["count"] == 3