neo-cortex2-mcp 6.1.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 (295) hide show
  1. neo_cortex2_mcp-6.1.0/.gitignore +20 -0
  2. neo_cortex2_mcp-6.1.0/PKG-INFO +24 -0
  3. neo_cortex2_mcp-6.1.0/mbel.md +59 -0
  4. neo_cortex2_mcp-6.1.0/pyproject.toml +74 -0
  5. neo_cortex2_mcp-6.1.0/src/neo_cortex/__init__.py +0 -0
  6. neo_cortex2_mcp-6.1.0/src/neo_cortex/assets/__init__.py +10 -0
  7. neo_cortex2_mcp-6.1.0/src/neo_cortex/assets/default-gitignore +6 -0
  8. neo_cortex2_mcp-6.1.0/src/neo_cortex/assets/default-models.json +28 -0
  9. neo_cortex2_mcp-6.1.0/src/neo_cortex/assets/docker-compose.yml +37 -0
  10. neo_cortex2_mcp-6.1.0/src/neo_cortex/cli/__init__.py +1 -0
  11. neo_cortex2_mcp-6.1.0/src/neo_cortex/cli/dashboard_v2.py +78 -0
  12. neo_cortex2_mcp-6.1.0/src/neo_cortex/cli/import_logs.py +175 -0
  13. neo_cortex2_mcp-6.1.0/src/neo_cortex/cli/init.py +77 -0
  14. neo_cortex2_mcp-6.1.0/src/neo_cortex/cli/mcp_server_v2.py +252 -0
  15. neo_cortex2_mcp-6.1.0/src/neo_cortex/cli/rebuild_cli.py +262 -0
  16. neo_cortex2_mcp-6.1.0/src/neo_cortex/cli/status.py +94 -0
  17. neo_cortex2_mcp-6.1.0/src/neo_cortex/cli/stop_hook.py +51 -0
  18. neo_cortex2_mcp-6.1.0/src/neo_cortex/cli/timeline_v2.py +228 -0
  19. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/__init__.py +7 -0
  20. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/bundle.py +196 -0
  21. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/config/__init__.py +27 -0
  22. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/config/project_discovery.py +112 -0
  23. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/config/schema.py +360 -0
  24. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/doctor/__init__.py +1 -0
  25. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/doctor/service.py +142 -0
  26. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/embedder/__init__.py +4 -0
  27. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/embedder/jina.py +101 -0
  28. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/hooks.py +69 -0
  29. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/integrity/__init__.py +2 -0
  30. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/integrity/scanner.py +73 -0
  31. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/llm/__init__.py +11 -0
  32. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/llm/executor.py +171 -0
  33. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/llm/factory.py +134 -0
  34. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/llm/providers/__init__.py +4 -0
  35. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/llm/providers/anthropic.py +200 -0
  36. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/llm/providers/groq.py +117 -0
  37. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/llm/providers/openrouter.py +131 -0
  38. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/llm/transport_retry.py +78 -0
  39. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/llm/validators.py +41 -0
  40. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/logging/__init__.py +24 -0
  41. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/logging/context.py +37 -0
  42. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/logging/setup.py +139 -0
  43. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/__init__.py +4 -0
  44. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/article_prompt.py +99 -0
  45. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/article_validator.py +175 -0
  46. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/articulator_v2.py +154 -0
  47. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/compress_mbel.py +31 -0
  48. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/dtos.py +40 -0
  49. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/manual_path.py +73 -0
  50. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/persist_articulation.py +69 -0
  51. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/prompt_helpers.py +24 -0
  52. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/prompts/__init__.py +10 -0
  53. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/prompts/_versions/README.md +18 -0
  54. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/prompts/_versions/relatore_BASE.md +115 -0
  55. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/prompts/_versions/relatore_v1_delta_seconds.md +118 -0
  56. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/prompts/_versions/relatore_v2_precomputed_deltas.md +118 -0
  57. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/prompts/_versions/relatore_v3_strict_artifact.md +121 -0
  58. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/prompts/articulator.md +312 -0
  59. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/prompts/relatore.md +121 -0
  60. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/relate_jobs.py +202 -0
  61. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/relate_validator.py +96 -0
  62. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/relate_worker.py +156 -0
  63. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/relatore.py +306 -0
  64. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/retry.py +153 -0
  65. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/stop_hook.py +134 -0
  66. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/synth_lifecycle.py +126 -0
  67. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/transcript_cleanup.py +41 -0
  68. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/validators/__init__.py +0 -0
  69. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/pipeline/validators/identity_validator.py +57 -0
  70. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/primitives/__init__.py +8 -0
  71. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/primitives/drill.py +269 -0
  72. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/primitives/identity_review.py +162 -0
  73. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/primitives/ingest.py +124 -0
  74. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/primitives/projects.py +84 -0
  75. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/primitives/resume.py +251 -0
  76. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/primitives/search.py +404 -0
  77. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/primitives/timeline.py +89 -0
  78. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/setup.py +143 -0
  79. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/source/__init__.py +11 -0
  80. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/source/mongo.py +115 -0
  81. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/source/protocol.py +37 -0
  82. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/__init__.py +141 -0
  83. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/assertions.py +128 -0
  84. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/entities.py +214 -0
  85. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/protocols.py +294 -0
  86. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/__init__.py +11 -0
  87. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/_occ.py +36 -0
  88. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/_rid.py +54 -0
  89. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/article_store.py +376 -0
  90. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/build_jobs.py +101 -0
  91. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/concept_index.py +261 -0
  92. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/factory.py +130 -0
  93. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/integrity_probe.py +104 -0
  94. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/manual_log.py +47 -0
  95. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/pool.py +173 -0
  96. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/relate_queue.py +217 -0
  97. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/relation_graph.py +357 -0
  98. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/schema.py +462 -0
  99. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/synth_boundary.py +316 -0
  100. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/turn_log.py +164 -0
  101. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/surrealdb/worker_state.py +168 -0
  102. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/storage/utils.py +18 -0
  103. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/workers/__init__.py +13 -0
  104. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/workers/articulator_phase.py +106 -0
  105. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/workers/orchestrator.py +176 -0
  106. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/workers/phase_a.py +113 -0
  107. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/workers/relator_phase.py +83 -0
  108. neo_cortex2_mcp-6.1.0/src/neo_cortex/core/workers/turn_ingest.py +150 -0
  109. neo_cortex2_mcp-6.1.0/src/neo_cortex/cortex2_cli/__init__.py +6 -0
  110. neo_cortex2_mcp-6.1.0/src/neo_cortex/cortex2_cli/__main__.py +182 -0
  111. neo_cortex2_mcp-6.1.0/src/neo_cortex/cortex2_mcp/__init__.py +6 -0
  112. neo_cortex2_mcp-6.1.0/src/neo_cortex/cortex2_mcp/tools.py +354 -0
  113. neo_cortex2_mcp-6.1.0/src/neo_cortex/cortex2_web/__init__.py +6 -0
  114. neo_cortex2_mcp-6.1.0/src/neo_cortex/cortex2_web/dashboard.py +234 -0
  115. neo_cortex2_mcp-6.1.0/src/neo_cortex/cortex2_web/static/index.html +1097 -0
  116. neo_cortex2_mcp-6.1.0/src/neo_cortex/mbel.md +59 -0
  117. neo_cortex2_mcp-6.1.0/tests/__init__.py +0 -0
  118. neo_cortex2_mcp-6.1.0/tests/_article_factory.py +41 -0
  119. neo_cortex2_mcp-6.1.0/tests/_surreal_instance.py +74 -0
  120. neo_cortex2_mcp-6.1.0/tests/conftest.py +176 -0
  121. neo_cortex2_mcp-6.1.0/tests/e2e/__init__.py +0 -0
  122. neo_cortex2_mcp-6.1.0/tests/e2e/conftest.py +278 -0
  123. neo_cortex2_mcp-6.1.0/tests/e2e/test_cli.py +848 -0
  124. neo_cortex2_mcp-6.1.0/tests/e2e/test_crash_recovery.py +306 -0
  125. neo_cortex2_mcp-6.1.0/tests/e2e/test_edge_cases.py +253 -0
  126. neo_cortex2_mcp-6.1.0/tests/e2e/test_fixture.py +94 -0
  127. neo_cortex2_mcp-6.1.0/tests/e2e/test_full_pipeline_flow.py +146 -0
  128. neo_cortex2_mcp-6.1.0/tests/e2e/test_llm_errors.py +638 -0
  129. neo_cortex2_mcp-6.1.0/tests/e2e/test_maintenance_lifecycle.py +168 -0
  130. neo_cortex2_mcp-6.1.0/tests/e2e/test_mcp_tools.py +994 -0
  131. neo_cortex2_mcp-6.1.0/tests/e2e/test_pipeline_atomics.py +336 -0
  132. neo_cortex2_mcp-6.1.0/tests/e2e/test_rebuild_cycle.py +219 -0
  133. neo_cortex2_mcp-6.1.0/tests/e2e/test_stress.py +231 -0
  134. neo_cortex2_mcp-6.1.0/tests/e2e/test_worker_phases.py +269 -0
  135. neo_cortex2_mcp-6.1.0/tests/fixtures/conversation_log_snapshot.db +0 -0
  136. neo_cortex2_mcp-6.1.0/tests/fixtures/cortex_db_snapshot/5844946a-6106-4ad5-8726-20afd3d66c9e/data_level0.bin +0 -0
  137. neo_cortex2_mcp-6.1.0/tests/fixtures/cortex_db_snapshot/5844946a-6106-4ad5-8726-20afd3d66c9e/header.bin +0 -0
  138. neo_cortex2_mcp-6.1.0/tests/fixtures/cortex_db_snapshot/5844946a-6106-4ad5-8726-20afd3d66c9e/index_metadata.pickle +0 -0
  139. neo_cortex2_mcp-6.1.0/tests/fixtures/cortex_db_snapshot/5844946a-6106-4ad5-8726-20afd3d66c9e/length.bin +0 -0
  140. neo_cortex2_mcp-6.1.0/tests/fixtures/cortex_db_snapshot/5844946a-6106-4ad5-8726-20afd3d66c9e/link_lists.bin +0 -0
  141. neo_cortex2_mcp-6.1.0/tests/fixtures/cortex_db_snapshot/chroma.sqlite3 +0 -0
  142. neo_cortex2_mcp-6.1.0/tests/fixtures/cortex_projects_with_descriptions.json +34 -0
  143. neo_cortex2_mcp-6.1.0/tests/fixtures/poc_synth_ids.txt +12 -0
  144. neo_cortex2_mcp-6.1.0/tests/fixtures/regression_articles.json +186 -0
  145. neo_cortex2_mcp-6.1.0/tests/integration/__init__.py +0 -0
  146. neo_cortex2_mcp-6.1.0/tests/integration/algo/__init__.py +0 -0
  147. neo_cortex2_mcp-6.1.0/tests/integration/algo/test_primitives_v3.py +96 -0
  148. neo_cortex2_mcp-6.1.0/tests/integration/cli/__init__.py +0 -0
  149. neo_cortex2_mcp-6.1.0/tests/integration/cli/test_import_logs.py +173 -0
  150. neo_cortex2_mcp-6.1.0/tests/integration/cli/test_init_status.py +121 -0
  151. neo_cortex2_mcp-6.1.0/tests/integration/cli/test_stop_hook_v2.py +30 -0
  152. neo_cortex2_mcp-6.1.0/tests/integration/conftest.py +45 -0
  153. neo_cortex2_mcp-6.1.0/tests/integration/e2e/__init__.py +0 -0
  154. neo_cortex2_mcp-6.1.0/tests/integration/e2e/test_e2e_v3.py +147 -0
  155. neo_cortex2_mcp-6.1.0/tests/integration/integrity/__init__.py +0 -0
  156. neo_cortex2_mcp-6.1.0/tests/integration/integrity/test_integrity_scan.py +106 -0
  157. neo_cortex2_mcp-6.1.0/tests/integration/pipeline/__init__.py +0 -0
  158. neo_cortex2_mcp-6.1.0/tests/integration/pipeline/test_stop_hook.py +207 -0
  159. neo_cortex2_mcp-6.1.0/tests/integration/pipeline/test_synth_lifecycle.py +119 -0
  160. neo_cortex2_mcp-6.1.0/tests/integration/primitives/__init__.py +0 -0
  161. neo_cortex2_mcp-6.1.0/tests/integration/primitives/test_cortex_drill.py +144 -0
  162. neo_cortex2_mcp-6.1.0/tests/integration/primitives/test_cortex_ingest.py +201 -0
  163. neo_cortex2_mcp-6.1.0/tests/integration/primitives/test_cortex_projects.py +194 -0
  164. neo_cortex2_mcp-6.1.0/tests/integration/primitives/test_cortex_resume.py +94 -0
  165. neo_cortex2_mcp-6.1.0/tests/integration/primitives/test_cortex_search.py +215 -0
  166. neo_cortex2_mcp-6.1.0/tests/integration/primitives/test_cortex_timeline.py +149 -0
  167. neo_cortex2_mcp-6.1.0/tests/integration/rebuild/__init__.py +0 -0
  168. neo_cortex2_mcp-6.1.0/tests/integration/rebuild/test_rebuild_v3.py +107 -0
  169. neo_cortex2_mcp-6.1.0/tests/integration/scripts/__init__.py +0 -0
  170. neo_cortex2_mcp-6.1.0/tests/integration/scripts/test_scripts_pool.py +55 -0
  171. neo_cortex2_mcp-6.1.0/tests/integration/smoke/__init__.py +0 -0
  172. neo_cortex2_mcp-6.1.0/tests/integration/smoke/test_mcp_dashboard_v3.py +46 -0
  173. neo_cortex2_mcp-6.1.0/tests/integration/storage/__init__.py +0 -0
  174. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_article_store_query_fix.py +135 -0
  175. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_article_store_v3.py +69 -0
  176. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_assertions.py +147 -0
  177. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_boot_v3.py +67 -0
  178. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_build_jobs.py +97 -0
  179. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_clear_for_project.py +122 -0
  180. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_concept_index_v3.py +64 -0
  181. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_concurrency_production.py +245 -0
  182. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_factory_boot_pool.py +180 -0
  183. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_lock_race.py +84 -0
  184. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_manual_log.py +74 -0
  185. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_persist_conflict_robustness.py +182 -0
  186. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_pool_ws_concurrency.py +69 -0
  187. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_pool_ws_robustness.py +126 -0
  188. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_post_write_assertions.py +135 -0
  189. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_provider_consolidation.py +12 -0
  190. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_queue_claim_atomic.py +87 -0
  191. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_relation_graph_v3.py +79 -0
  192. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_return_before_bulk.py +149 -0
  193. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_schema_apply_concurrent.py +79 -0
  194. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_schema_on_pool.py +125 -0
  195. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_schema_v3.py +106 -0
  196. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_search_lean_projection.py +134 -0
  197. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_storage_backend_surrealdb_e2e.py +116 -0
  198. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_surreal_article_store.py +327 -0
  199. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_surreal_concept_index.py +297 -0
  200. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_surreal_pool.py +137 -0
  201. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_surreal_relate_queue.py +159 -0
  202. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_surreal_relation_graph.py +401 -0
  203. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_surreal_schema.py +1034 -0
  204. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_surreal_synth_boundary.py +181 -0
  205. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_surreal_turn_log.py +112 -0
  206. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_surreal_worker_state.py +105 -0
  207. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_synth_claim_articulating.py +119 -0
  208. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_synth_extend_atomic.py +95 -0
  209. neo_cortex2_mcp-6.1.0/tests/integration/storage/test_worker_state_upsert_atomic.py +126 -0
  210. neo_cortex2_mcp-6.1.0/tests/integration/test_orphan_guard.py +88 -0
  211. neo_cortex2_mcp-6.1.0/tests/integration/test_prompt_quality.py +431 -0
  212. neo_cortex2_mcp-6.1.0/tests/integration/test_surreal_test_instance.py +97 -0
  213. neo_cortex2_mcp-6.1.0/tests/integration/test_validator_regression.py +101 -0
  214. neo_cortex2_mcp-6.1.0/tests/integration/web/__init__.py +0 -0
  215. neo_cortex2_mcp-6.1.0/tests/integration/web/test_dashboard_smoke.py +255 -0
  216. neo_cortex2_mcp-6.1.0/tests/integration/workers/__init__.py +0 -0
  217. neo_cortex2_mcp-6.1.0/tests/integration/workers/test_orchestrator_integration.py +139 -0
  218. neo_cortex2_mcp-6.1.0/tests/integration/workers/test_workers_v3.py +73 -0
  219. neo_cortex2_mcp-6.1.0/tests/unit/__init__.py +0 -0
  220. neo_cortex2_mcp-6.1.0/tests/unit/architecture/__init__.py +0 -0
  221. neo_cortex2_mcp-6.1.0/tests/unit/architecture/test_connection_removed.py +76 -0
  222. neo_cortex2_mcp-6.1.0/tests/unit/architecture/test_no_db_access_without_pool.py +123 -0
  223. neo_cortex2_mcp-6.1.0/tests/unit/architecture/test_no_handrolled_pool_in_tests.py +50 -0
  224. neo_cortex2_mcp-6.1.0/tests/unit/architecture/test_no_warning_suppression.py +95 -0
  225. neo_cortex2_mcp-6.1.0/tests/unit/architecture/test_prod_safety_guard.py +41 -0
  226. neo_cortex2_mcp-6.1.0/tests/unit/architecture/test_single_pool_provider.py +44 -0
  227. neo_cortex2_mcp-6.1.0/tests/unit/architecture/test_storage_isolation.py +143 -0
  228. neo_cortex2_mcp-6.1.0/tests/unit/architecture/test_test_groups.py +108 -0
  229. neo_cortex2_mcp-6.1.0/tests/unit/cli/__init__.py +0 -0
  230. neo_cortex2_mcp-6.1.0/tests/unit/cli/test_dashboard_v2.py +83 -0
  231. neo_cortex2_mcp-6.1.0/tests/unit/cli/test_mcp_server_surrealdb_check.py +23 -0
  232. neo_cortex2_mcp-6.1.0/tests/unit/cli/test_mcp_server_v2.py +179 -0
  233. neo_cortex2_mcp-6.1.0/tests/unit/cli/test_rebuild_cli.py +196 -0
  234. neo_cortex2_mcp-6.1.0/tests/unit/cli/test_rebuild_cli_protocol.py +28 -0
  235. neo_cortex2_mcp-6.1.0/tests/unit/cli/test_stop_hook_signal.py +56 -0
  236. neo_cortex2_mcp-6.1.0/tests/unit/cli/test_timeline_v2.py +163 -0
  237. neo_cortex2_mcp-6.1.0/tests/unit/config/__init__.py +0 -0
  238. neo_cortex2_mcp-6.1.0/tests/unit/config/test_project_discovery.py +142 -0
  239. neo_cortex2_mcp-6.1.0/tests/unit/config/test_projects_config_with_description.py +62 -0
  240. neo_cortex2_mcp-6.1.0/tests/unit/core/__init__.py +0 -0
  241. neo_cortex2_mcp-6.1.0/tests/unit/core/llm/__init__.py +0 -0
  242. neo_cortex2_mcp-6.1.0/tests/unit/core/llm/test_executor.py +230 -0
  243. neo_cortex2_mcp-6.1.0/tests/unit/core/llm/test_openrouter_session.py +117 -0
  244. neo_cortex2_mcp-6.1.0/tests/unit/core/llm/test_validators_protocol.py +51 -0
  245. neo_cortex2_mcp-6.1.0/tests/unit/core/source/__init__.py +0 -0
  246. neo_cortex2_mcp-6.1.0/tests/unit/core/source/test_mongo.py +168 -0
  247. neo_cortex2_mcp-6.1.0/tests/unit/core/source/test_protocol.py +49 -0
  248. neo_cortex2_mcp-6.1.0/tests/unit/core/test_auto_setup.py +136 -0
  249. neo_cortex2_mcp-6.1.0/tests/unit/core/test_bundle.py +203 -0
  250. neo_cortex2_mcp-6.1.0/tests/unit/core/test_config_workers.py +44 -0
  251. neo_cortex2_mcp-6.1.0/tests/unit/core/test_cortex_config.py +265 -0
  252. neo_cortex2_mcp-6.1.0/tests/unit/core/test_hooks.py +68 -0
  253. neo_cortex2_mcp-6.1.0/tests/unit/core/test_llm_factory.py +79 -0
  254. neo_cortex2_mcp-6.1.0/tests/unit/core/test_logging.py +164 -0
  255. neo_cortex2_mcp-6.1.0/tests/unit/core/test_logging_no_warning.py +57 -0
  256. neo_cortex2_mcp-6.1.0/tests/unit/core/test_logging_quiet_loggers.py +42 -0
  257. neo_cortex2_mcp-6.1.0/tests/unit/core/test_setup_logging_defaults.py +19 -0
  258. neo_cortex2_mcp-6.1.0/tests/unit/core/test_storage_factory.py +188 -0
  259. neo_cortex2_mcp-6.1.0/tests/unit/core/workers/__init__.py +0 -0
  260. neo_cortex2_mcp-6.1.0/tests/unit/mcp/__init__.py +0 -0
  261. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/__init__.py +0 -0
  262. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_apply_turn_to_synth.py +156 -0
  263. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_article_schema_no_length_constraints.py +43 -0
  264. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_article_validator.py +276 -0
  265. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_articulator_template_no_examples.py +61 -0
  266. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_articulator_v2.py +181 -0
  267. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_manual_path.py +135 -0
  268. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_prompt_helpers.py +74 -0
  269. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_relate_jobs.py +376 -0
  270. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_relate_validator.py +91 -0
  271. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_relatore.py +208 -0
  272. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_retry.py +188 -0
  273. neo_cortex2_mcp-6.1.0/tests/unit/pipeline/test_transcript_cleanup.py +67 -0
  274. neo_cortex2_mcp-6.1.0/tests/unit/primitives/__init__.py +0 -0
  275. neo_cortex2_mcp-6.1.0/tests/unit/primitives/test_cortex_identity_review.py +183 -0
  276. neo_cortex2_mcp-6.1.0/tests/unit/storage/__init__.py +0 -0
  277. neo_cortex2_mcp-6.1.0/tests/unit/storage/test_entity_article_strict.py +81 -0
  278. neo_cortex2_mcp-6.1.0/tests/unit/storage/test_entity_relate_queue.py +83 -0
  279. neo_cortex2_mcp-6.1.0/tests/unit/storage/test_pool_lockless.py +26 -0
  280. neo_cortex2_mcp-6.1.0/tests/unit/storage/test_pool_url_guard.py +35 -0
  281. neo_cortex2_mcp-6.1.0/tests/unit/storage/test_protocols.py +230 -0
  282. neo_cortex2_mcp-6.1.0/tests/unit/storage/test_rid_helpers.py +74 -0
  283. neo_cortex2_mcp-6.1.0/tests/unit/storage/test_schema_v3_guards.py +72 -0
  284. neo_cortex2_mcp-6.1.0/tests/unit/test_no_silent_exceptions.py +37 -0
  285. neo_cortex2_mcp-6.1.0/tests/unit/web/__init__.py +0 -0
  286. neo_cortex2_mcp-6.1.0/tests/unit/web/test_dashboard.py +270 -0
  287. neo_cortex2_mcp-6.1.0/tests/unit/workers/__init__.py +0 -0
  288. neo_cortex2_mcp-6.1.0/tests/unit/workers/test_articulator_exactly_once.py +173 -0
  289. neo_cortex2_mcp-6.1.0/tests/unit/workers/test_guard_maintenance.py +129 -0
  290. neo_cortex2_mcp-6.1.0/tests/unit/workers/test_mcp_integration.py +115 -0
  291. neo_cortex2_mcp-6.1.0/tests/unit/workers/test_orchestrator.py +130 -0
  292. neo_cortex2_mcp-6.1.0/tests/unit/workers/test_phase_a.py +169 -0
  293. neo_cortex2_mcp-6.1.0/tests/unit/workers/test_phases.py +179 -0
  294. neo_cortex2_mcp-6.1.0/tests/unit/workers/test_rebuild_tool_complete.py +115 -0
  295. neo_cortex2_mcp-6.1.0/tests/unit/workers/test_turn_ingest.py +168 -0
@@ -0,0 +1,20 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ logs/
9
+ neo-cortex.log
10
+ cortex_db/
11
+ neo-cortex.log.1
12
+ *.log.1
13
+ .lsai/
14
+
15
+ # Postgres dev volume
16
+ pgdata/
17
+ .env
18
+
19
+ # Throwaway forensic probes / experiments (never committed)
20
+ tmp/
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: neo-cortex2-mcp
3
+ Version: 6.1.0
4
+ Summary: Neo Memory Cortex — Standalone Memory Server
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: fastapi>=0.115.0
7
+ Requires-Dist: fastmcp>=2.14.0
8
+ Requires-Dist: httpx>=0.28.0
9
+ Requires-Dist: pydantic-settings>=2.0.0
10
+ Requires-Dist: pydantic>=2.10.0
11
+ Requires-Dist: structlog>=24.0.0
12
+ Requires-Dist: surrealdb>=2.0.0
13
+ Requires-Dist: uvicorn>=0.34.0
14
+ Requires-Dist: watchfiles>=1.0.0
15
+ Provides-Extra: anthropic
16
+ Requires-Dist: anthropic>=0.40.0; extra == 'anthropic'
17
+ Provides-Extra: claude
18
+ Requires-Dist: claude-code-sdk>=0.0.1; extra == 'claude'
19
+ Provides-Extra: dev
20
+ Requires-Dist: pyright>=1.1.0; extra == 'dev'
21
+ Requires-Dist: pytest-asyncio>=0.25.0; extra == 'dev'
22
+ Requires-Dist: pytest-httpx>=0.35.0; extra == 'dev'
23
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
24
+ Requires-Dist: pyyaml>=6.0; extra == 'dev'
@@ -0,0 +1,59 @@
1
+ # MBEL v5 Grammar [IMMUTABLE]
2
+
3
+ §MBEL:5.0
4
+ @purpose::AIMemoryEncoding{compression%75,fidelity%100}
5
+
6
+ ## Operators [IMMUTABLE]
7
+
8
+ ### Core Set (27)
9
+ ```
10
+ [Temporal:4]
11
+ > past/did/changed/was
12
+ @ present/current/now/is
13
+ ? future/todo/will/planned
14
+ ≈ around/circa/approximate/roughly
15
+
16
+ [State:4]
17
+ ✓ complete/done/success/working
18
+ ✗ failed/broken/error/bug
19
+ ! critical/important/priority/alert
20
+ ⚡ active/urgent/in-progress/hot
21
+
22
+ [Relation:6]
23
+ :: is/defines/equals/becomes
24
+ → leads_to/causes/then/produces
25
+ ← comes_from/because/source/from
26
+ ↔ mutual/bidirectional/syncs/relates
27
+ + and/with/combines/includes
28
+ - remove/delete/without/except
29
+
30
+ [Structure:5]
31
+ [] section/category/namespace/group
32
+ {} metadata/attributes/properties/details
33
+ () note/comment/aside/remark
34
+ | or/alternative/choice/option
35
+ <> variant/template/placeholder/variable
36
+
37
+ [Quantification:3]
38
+ # number/count/quantity/amount
39
+ % percentage/ratio/proportion/part
40
+ ~ approximately/range/between/around
41
+
42
+ [Logic:3]
43
+ & AND/requires/must-have/with
44
+ || OR/either/can-be/allows
45
+ ¬ NOT/exclude/prevent/disable
46
+
47
+ [Meta:2]
48
+ © source/origin/author/credit
49
+ § version/revision/protocol/schema
50
+ ```
51
+
52
+ ### Grammar Rules [IMMUTABLE]
53
+ 1. NoArticles(a/the/an)
54
+ 2. CamelCase→MultiWord
55
+ 3. ImplicitSubject{contextClear}
56
+ 4. OperatorsOnly¬Punctuation
57
+ 5. Newline::StatementSeparator
58
+ 6. LatestOverridesPrevious
59
+ 7. LeftToRight→Composition
@@ -0,0 +1,74 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "neo-cortex2-mcp"
7
+ version = "6.1.0"
8
+ description = "Neo Memory Cortex — Standalone Memory Server"
9
+ requires-python = ">=3.12"
10
+ dependencies = [
11
+ "fastapi>=0.115.0",
12
+ "uvicorn>=0.34.0",
13
+ "httpx>=0.28.0",
14
+ "pydantic>=2.10.0",
15
+ "fastmcp>=2.14.0",
16
+ "structlog>=24.0.0",
17
+ "pydantic-settings>=2.0.0",
18
+ "surrealdb>=2.0.0",
19
+ "watchfiles>=1.0.0",
20
+ ]
21
+
22
+ [project.scripts]
23
+ neo-cortex2-stop-hook = "neo_cortex.cli.stop_hook:cli"
24
+ neo-cortex2-mcp = "neo_cortex.cli.mcp_server_v2:cli"
25
+ neo-cortex2-dashboard = "neo_cortex.cli.dashboard_v2:cli"
26
+ neo-cortex2-import = "neo_cortex.cli.import_logs:cli"
27
+ neo-cortex2-rebuild = "neo_cortex.cli.rebuild_cli:cli"
28
+ neo-cortex2-timeline = "neo_cortex.cli.timeline_v2:cli"
29
+
30
+ [project.optional-dependencies]
31
+ claude = ["claude-code-sdk>=0.0.1"]
32
+ anthropic = ["anthropic>=0.40.0"]
33
+ dev = [
34
+ "pytest>=8.0.0",
35
+ "pytest-asyncio>=0.25.0",
36
+ "pytest-httpx>=0.35.0",
37
+ "pyright>=1.1.0",
38
+ "pyyaml>=6.0",
39
+ ]
40
+
41
+ [tool.hatch.build.targets.wheel]
42
+ packages = ["src/neo_cortex"]
43
+
44
+ [tool.hatch.build.targets.wheel.shared-data]
45
+ "src/neo_cortex/core/pipeline/prompts" = "neo_cortex/core/pipeline/prompts"
46
+
47
+ [tool.hatch.build.targets.sdist]
48
+ include = ["src/neo_cortex/**", "mbel.md", "tests/**", "pyproject.toml"]
49
+
50
+ [tool.pytest.ini_options]
51
+ asyncio_mode = "auto"
52
+ testpaths = ["tests"]
53
+ markers = [
54
+ "unit: fast in-process unit test, no external dependencies",
55
+ "integration: requires external service (SurrealDB, etc.)",
56
+ "slow: long-running test, opt-in via -m slow",
57
+ "live_llm: hits a live LLM provider (OpenRouter); gated by RUN_LIVE_LLM_TESTS=1",
58
+ "e2e: end-to-end test with real SurrealDB, fake LLM/embedder",
59
+ ]
60
+ filterwarnings = [
61
+ "error",
62
+ # CAUSA B — finalizzazione GC dell'async-generator `Connection.__aiter__` di websockets
63
+ # (v15.0.1, asyncio API), iterato in `_recv_task` del SDK surrealdb-py
64
+ # (`connections/async_ws.py:59` `async for data in self.socket`), finalizzato dal GC DOPO
65
+ # la chiusura del loop per-test di pytest-asyncio. NON è un leak nostro:
66
+ # misurato 1164 connessioni aperte = 1164 `close()` chiamate (0 non chiuse). Upstream, benigna.
67
+ # Pattern scoped al repr esatto dell'async-gen; il `.*` copre il prefisso "Exception ignored in:"
68
+ # (evita il `:` come separatore di campo). Verificato: matcha solo questo async-gen, non altri.
69
+ "ignore:.*async_generator object Connection\\.__aiter__:pytest.PytestUnraisableExceptionWarning",
70
+ ]
71
+
72
+ [tool.pyright]
73
+ typeCheckingMode = "strict"
74
+ pythonVersion = "3.12"
File without changes
@@ -0,0 +1,10 @@
1
+ """Bundled assets for auto-setup (compose, model profiles, gitignore)."""
2
+ from __future__ import annotations
3
+
4
+ import importlib.resources
5
+ from pathlib import Path
6
+
7
+
8
+ def get_asset(name: str) -> Path:
9
+ """Return the filesystem path to a bundled asset file."""
10
+ return importlib.resources.files(__package__) / name
@@ -0,0 +1,6 @@
1
+ *.env
2
+ .env
3
+ cortex2.log
4
+ cortex2_state/
5
+ ingest-wake
6
+ hooks-installed
@@ -0,0 +1,28 @@
1
+ {
2
+ "active": "openrouter_default",
3
+ "profiles": {
4
+ "openrouter_default": {
5
+ "name": "DeepSeek v4 Flash via OpenRouter",
6
+ "description": "Default profile. Fast, cheap, high quality articulation.",
7
+ "provider": "openrouter",
8
+ "model": "deepseek/deepseek-v4-flash",
9
+ "reasoning_effort": "high",
10
+ "max_tokens": 300000
11
+ },
12
+ "gemini": {
13
+ "name": "Gemini 2.5 Flash",
14
+ "description": "Google Gemini via OpenRouter. Good quality, very cheap.",
15
+ "provider": "openrouter",
16
+ "model": "google/gemini-2.5-flash",
17
+ "reasoning_effort": "high",
18
+ "max_tokens": 65000
19
+ },
20
+ "groq": {
21
+ "name": "Llama 4 Scout via Groq",
22
+ "description": "Fastest inference. Groq direct API, no OpenRouter.",
23
+ "provider": "groq",
24
+ "model": "meta-llama/llama-4-scout-17b-16e-instruct",
25
+ "max_tokens": 8192
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,37 @@
1
+ name: cortex2
2
+
3
+ services:
4
+ surrealdb:
5
+ image: surrealdb/surrealdb:v3.0.5
6
+ container_name: cortex-surrealdb
7
+ user: "0:0"
8
+ command: start --user ${SURREAL_USER:-root} --pass ${SURREAL_PASS:-root} rocksdb:/data/srdb
9
+ ports:
10
+ - "${SURREAL_PORT:-8000}:8000"
11
+ volumes:
12
+ - surreal-data:/data
13
+ healthcheck:
14
+ test: ["CMD", "/surreal", "is-ready"]
15
+ interval: 5s
16
+ timeout: 3s
17
+ retries: 10
18
+ restart: unless-stopped
19
+
20
+ dashboard:
21
+ image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim
22
+ container_name: cortex-dashboard
23
+ command: uvx --from neo-cortex-mcp neo-cortex2-dashboard
24
+ ports:
25
+ - "${DASHBOARD_PORT:-5075}:5075"
26
+ env_file: .env
27
+ environment:
28
+ UV_PRERELEASE: allow
29
+ CORTEX_STORAGE__SURREALDB_URL: http://surrealdb:8000
30
+ CORTEX2_DASHBOARD_HOST: "0.0.0.0"
31
+ depends_on:
32
+ surrealdb:
33
+ condition: service_healthy
34
+ restart: unless-stopped
35
+
36
+ volumes:
37
+ surreal-data:
@@ -0,0 +1 @@
1
+ """Command-line entry points for cortex2."""
@@ -0,0 +1,78 @@
1
+ """cortex2 dashboard entrypoint — read-only FastAPI on port 5075.
2
+
3
+ Boots `cortex2_web.dashboard.build_app(bundle)` against the same
4
+ storage bundle the MCP server uses. NO worker tasks (the dashboard is
5
+ read-only — it observes what the workers produce, never schedules work
6
+ itself).
7
+
8
+ Listens on port 5075 by default to coexist with the legacy dashboard on
9
+ 5074. Override via CORTEX2_DASHBOARD_PORT / CORTEX2_DASHBOARD_HOST.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import asyncio
15
+ import os
16
+ import sys
17
+
18
+ import structlog
19
+ import uvicorn
20
+
21
+ from neo_cortex.core.bundle import build_bundle, close_bundle
22
+ from neo_cortex.core.config.schema import CortexConfig
23
+ from neo_cortex.core.logging import configure_logging
24
+ from neo_cortex.cortex2_web.dashboard import build_app
25
+
26
+ log = structlog.get_logger(__name__)
27
+
28
+
29
+ def _resolve_host() -> str:
30
+ return os.environ.get("CORTEX2_DASHBOARD_HOST", "127.0.0.1")
31
+
32
+
33
+ def _resolve_port() -> int:
34
+ raw = os.environ.get("CORTEX2_DASHBOARD_PORT", "5075")
35
+ try:
36
+ return int(raw)
37
+ except ValueError:
38
+ return 5075
39
+
40
+
41
+ async def _build_app_async() -> tuple:
42
+ config = CortexConfig.load()
43
+ configure_logging(config.logging)
44
+ bundle = await build_bundle(config)
45
+ app = build_app(bundle)
46
+ return app, bundle
47
+
48
+
49
+ def cli(argv: list[str] | None = None) -> int:
50
+ parser = argparse.ArgumentParser(
51
+ description="cortex2 dashboard — read-only FastAPI",
52
+ )
53
+ parser.add_argument("--host", default=None)
54
+ parser.add_argument("--port", type=int, default=None)
55
+ args = parser.parse_args(argv)
56
+
57
+ host = args.host or _resolve_host()
58
+ port = args.port or _resolve_port()
59
+
60
+ try:
61
+ app, bundle = asyncio.run(_build_app_async())
62
+ except Exception as e: # noqa: BLE001
63
+ log.error("cortex2.dashboard.init_failed", error=str(e))
64
+ return 1
65
+
66
+ log.info("cortex2.dashboard.starting", host=host, port=port)
67
+ try:
68
+ uvicorn.run(app, host=host, port=port, log_level="info")
69
+ finally:
70
+ try:
71
+ asyncio.run(close_bundle(bundle))
72
+ except Exception: # noqa: BLE001
73
+ pass
74
+ return 0
75
+
76
+
77
+ if __name__ == "__main__":
78
+ raise SystemExit(cli())
@@ -0,0 +1,175 @@
1
+ """cortex2 importer — drain a legacy ConversationLog into bundle.turn_log.
2
+
3
+ Source-agnostic: accepts any IConversationLogSource implementation.
4
+ Today: `--source mongo --uri ... --db ... --ws ...` builds a
5
+ MongoConversationLogSource. Tomorrow: `--source sqlite --path ...` will
6
+ build the SQLite adapter (Plan 35) without changing this CLI.
7
+
8
+ Idempotent: bundle.turn_log.append_if_new uses content_hash UNIQUE — a
9
+ re-run on the same source skips already-imported turns. `--since-ts`
10
+ acts as a coarse cursor for resume.
11
+
12
+ Progress: structlog event every PROGRESS_EVERY rows.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import argparse
17
+ import asyncio
18
+ import sys
19
+ from typing import Optional
20
+
21
+ import structlog
22
+
23
+ from neo_cortex.core.bundle import build_bundle, close_bundle
24
+ from neo_cortex.core.config.schema import CortexConfig
25
+ from neo_cortex.core.logging import configure_logging
26
+ from neo_cortex.core.source import IConversationLogSource
27
+ from neo_cortex.core.storage.entities import Turn
28
+ from neo_cortex.core.storage.utils import compute_content_hash
29
+
30
+ log = structlog.get_logger(__name__)
31
+
32
+ PROGRESS_EVERY = 200
33
+
34
+
35
+ async def _drain(
36
+ source: IConversationLogSource,
37
+ bundle,
38
+ *,
39
+ session_id_filter: Optional[str],
40
+ since_ts: Optional[float],
41
+ limit: Optional[int],
42
+ ) -> dict:
43
+ n_seen = 0
44
+ n_inserted = 0
45
+ n_skipped = 0
46
+ n_errors = 0
47
+ last_ts = since_ts or 0.0
48
+
49
+ async for turn in source.iter_entries(
50
+ session_id_filter=session_id_filter,
51
+ since_ts=since_ts,
52
+ limit=limit,
53
+ ):
54
+ n_seen += 1
55
+ # Compute content_hash here so re-runs are idempotent regardless
56
+ # of whether the source provides the column.
57
+ if turn.content_hash is None:
58
+ turn = Turn(
59
+ session_id=turn.session_id,
60
+ ts=turn.ts,
61
+ role=turn.role,
62
+ content=turn.content,
63
+ content_hash=compute_content_hash(turn),
64
+ )
65
+ try:
66
+ if hasattr(bundle.turn_log, "append_if_new"):
67
+ inserted = await bundle.turn_log.append_if_new(turn)
68
+ if inserted:
69
+ n_inserted += 1
70
+ else:
71
+ n_skipped += 1
72
+ else:
73
+ await bundle.turn_log.append(turn)
74
+ n_inserted += 1
75
+ last_ts = max(last_ts, turn.ts)
76
+ except Exception as e: # noqa: BLE001 — keep draining on errors
77
+ n_errors += 1
78
+ log.warning(
79
+ "importer.row_failed",
80
+ ts=turn.ts, session=turn.session_id, error=str(e),
81
+ )
82
+
83
+ if n_seen % PROGRESS_EVERY == 0:
84
+ log.info(
85
+ "importer.progress",
86
+ seen=n_seen, inserted=n_inserted, skipped=n_skipped,
87
+ errors=n_errors, last_ts=last_ts,
88
+ )
89
+
90
+ return {
91
+ "seen": n_seen,
92
+ "inserted": n_inserted,
93
+ "skipped": n_skipped,
94
+ "errors": n_errors,
95
+ "last_ts": last_ts,
96
+ }
97
+
98
+
99
+ def _build_source(args: argparse.Namespace) -> IConversationLogSource:
100
+ if args.source == "mongo":
101
+ if not args.mongo_uri or not args.mongo_db:
102
+ raise SystemExit(
103
+ "--source mongo requires --mongo-uri and --mongo-db"
104
+ )
105
+ from neo_cortex.core.source.mongo import (
106
+ MongoConversationLogSource,
107
+ )
108
+ return MongoConversationLogSource(
109
+ mongo_uri=args.mongo_uri,
110
+ db_name=args.mongo_db,
111
+ ws_name=args.ws,
112
+ )
113
+ raise SystemExit(f"unknown --source: {args.source}")
114
+
115
+
116
+ async def _run(args: argparse.Namespace) -> int:
117
+ config = CortexConfig.load()
118
+ configure_logging(config.logging)
119
+ bundle = await build_bundle(config)
120
+ source = _build_source(args)
121
+ try:
122
+ stats = await _drain(
123
+ source, bundle,
124
+ session_id_filter=args.session_id,
125
+ since_ts=args.since_ts,
126
+ limit=args.limit,
127
+ )
128
+ log.info("importer.done", **stats)
129
+ print(
130
+ f"[importer] seen={stats['seen']} "
131
+ f"inserted={stats['inserted']} "
132
+ f"skipped(dup)={stats['skipped']} "
133
+ f"errors={stats['errors']} "
134
+ f"last_ts={stats['last_ts']}",
135
+ )
136
+ return 0 if stats["errors"] == 0 else 2
137
+ finally:
138
+ await source.close()
139
+ await close_bundle(bundle)
140
+
141
+
142
+ def cli(argv: list[str] | None = None) -> int:
143
+ parser = argparse.ArgumentParser(
144
+ description="cortex2 importer — drain legacy ConversationLog into "
145
+ "bundle.turn_log via storage Protocol",
146
+ )
147
+ parser.add_argument(
148
+ "--source", choices=["mongo"], required=True,
149
+ help="adapter selector (sqlite/jsonl coming in Plan 35)",
150
+ )
151
+ parser.add_argument("--ws", required=True, help="workspace name")
152
+ parser.add_argument("--mongo-uri", help="mongo connection URI")
153
+ parser.add_argument("--mongo-db", help="mongo database name")
154
+ parser.add_argument("--session-id", default=None, help="filter to one session")
155
+ parser.add_argument(
156
+ "--since-ts", type=float, default=None,
157
+ help="resume cursor — skip turns with ts <= value",
158
+ )
159
+ parser.add_argument(
160
+ "--limit", type=int, default=None,
161
+ help="cap on total imported turns (smoke runs)",
162
+ )
163
+ args = parser.parse_args(argv)
164
+
165
+ try:
166
+ return asyncio.run(_run(args))
167
+ except KeyboardInterrupt:
168
+ return 130
169
+ except Exception as e: # noqa: BLE001
170
+ log.error("cortex2.import.fatal", error=str(e))
171
+ return 1
172
+
173
+
174
+ if __name__ == "__main__":
175
+ raise SystemExit(cli())
@@ -0,0 +1,77 @@
1
+ """cortex init — atomic setup for a new ws + subscribed projects.
2
+
3
+ Applies the LOGS_DDL to cortex_logs/<ws_name> and the PROJECTS_DDL to
4
+ cortex_projects/<each subscribed project>. Idempotent — safe to run
5
+ multiple times. Optionally writes a default `.cortex.env` if missing.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import argparse
10
+ import asyncio
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING, Optional
13
+
14
+ from neo_cortex.core.logging import get_logger
15
+ from neo_cortex.core.storage.surrealdb.schema import apply_schema
16
+
17
+ if TYPE_CHECKING:
18
+ from neo_cortex.core.storage.surrealdb.pool import SurrealPool
19
+
20
+ log = get_logger(__name__)
21
+
22
+
23
+ _DEFAULT_ENV = """\
24
+ # cortex2 environment
25
+ CORTEX_STORAGE__SURREALDB_URL=http://localhost:8000
26
+ CORTEX_STORAGE__SURREALDB_USER=root
27
+ CORTEX_STORAGE__SURREALDB_PASS=root
28
+ CORTEX_LLM__PROVIDER=openrouter
29
+ CORTEX_EMBEDDING__API_KEY=jina-key-here
30
+ """
31
+
32
+
33
+ async def cortex_init(
34
+ ws_name: str,
35
+ projects: list[str],
36
+ pool: "SurrealPool",
37
+ env_path: Optional[Path] = None,
38
+ *,
39
+ logs_ns: str = "cortex_logs",
40
+ projects_ns: str = "cortex_projects",
41
+ ) -> int:
42
+ """Returns 0 on success, non-zero on failure."""
43
+ await apply_schema(pool, logs_ns, ws_name)
44
+ for p in projects:
45
+ await apply_schema(pool, projects_ns, p)
46
+
47
+ if env_path is not None and not env_path.exists():
48
+ env_path.write_text(_DEFAULT_ENV)
49
+ log.info("cortex_init.env_written", path=str(env_path))
50
+
51
+ log.info(
52
+ "cortex_init.done",
53
+ ws=ws_name, n_projects=len(projects),
54
+ )
55
+ return 0
56
+
57
+
58
+ def main() -> int:
59
+ parser = argparse.ArgumentParser(description="cortex2 init")
60
+ parser.add_argument("--ws", required=True, help="workspace name")
61
+ parser.add_argument(
62
+ "--project", action="append", default=[],
63
+ help="subscribed project (repeatable)",
64
+ )
65
+ parser.add_argument(
66
+ "--env", help="path to write .cortex.env if missing",
67
+ )
68
+ args = parser.parse_args()
69
+ print(
70
+ f"cortex init --ws {args.ws} "
71
+ f"--projects {args.project} --env {args.env}"
72
+ )
73
+ print(
74
+ "Live wiring requires a SurrealPool — invoke cortex_init() "
75
+ "from the service container."
76
+ )
77
+ return 0