contextsynapse 1.0.0__py3-none-any.whl

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 (776) hide show
  1. contextsynapse/__init__.py +107 -0
  2. contextsynapse/__main__.py +9 -0
  3. contextsynapse/a2a/__init__.py +28 -0
  4. contextsynapse/a2a/client.py +120 -0
  5. contextsynapse/a2a/discovery.py +128 -0
  6. contextsynapse/a2a/handlers.py +164 -0
  7. contextsynapse/a2a/models.py +246 -0
  8. contextsynapse/a2a/streaming.py +92 -0
  9. contextsynapse/a2a/task_manager.py +275 -0
  10. contextsynapse/adapters/__init__.py +98 -0
  11. contextsynapse/adapters/_base.py +298 -0
  12. contextsynapse/adapters/autogen/__init__.py +4 -0
  13. contextsynapse/adapters/autogen/tools.py +34 -0
  14. contextsynapse/adapters/crewai/__init__.py +4 -0
  15. contextsynapse/adapters/crewai/tools.py +63 -0
  16. contextsynapse/adapters/langchain/__init__.py +19 -0
  17. contextsynapse/adapters/langchain/message_history.py +17 -0
  18. contextsynapse/adapters/langchain/retriever.py +158 -0
  19. contextsynapse/adapters/langchain/tools.py +43 -0
  20. contextsynapse/adapters/langgraph/__init__.py +21 -0
  21. contextsynapse/adapters/langgraph/checkpoint.py +223 -0
  22. contextsynapse/adapters/langgraph/context_tools.py +341 -0
  23. contextsynapse/adapters/langgraph/message_history.py +138 -0
  24. contextsynapse/adapters/langgraph/tools.py +43 -0
  25. contextsynapse/adapters/llamaindex/__init__.py +4 -0
  26. contextsynapse/adapters/llamaindex/tools.py +64 -0
  27. contextsynapse/adapters/openai/__init__.py +24 -0
  28. contextsynapse/adapters/openai/tools.py +206 -0
  29. contextsynapse/adapters/pydantic_ai/__init__.py +4 -0
  30. contextsynapse/adapters/pydantic_ai/tools.py +36 -0
  31. contextsynapse/adapters/swarm/__init__.py +4 -0
  32. contextsynapse/adapters/swarm/tools.py +44 -0
  33. contextsynapse/agents/__init__.py +6 -0
  34. contextsynapse/agents/auto_capture.py +80 -0
  35. contextsynapse/agents/dispatcher.py +299 -0
  36. contextsynapse/agents/notifications.py +112 -0
  37. contextsynapse/aiql/__init__.py +71 -0
  38. contextsynapse/aiql/cache.py +341 -0
  39. contextsynapse/aiql/compiler/__init__.py +62 -0
  40. contextsynapse/aiql/compiler/optimizer.py +126 -0
  41. contextsynapse/aiql/compiler/planner.py +366 -0
  42. contextsynapse/aiql/compiler/statistics.py +235 -0
  43. contextsynapse/aiql/compiler/validator.py +425 -0
  44. contextsynapse/aiql/engine/__init__.py +11 -0
  45. contextsynapse/aiql/engine/chunk_storage.py +2584 -0
  46. contextsynapse/aiql/engine/executor.py +7737 -0
  47. contextsynapse/aiql/engine/extraction_integration.py +442 -0
  48. contextsynapse/aiql/engine/graph_builder.py +405 -0
  49. contextsynapse/aiql/engine/node_creation_from_normalized.py +691 -0
  50. contextsynapse/aiql/engine/performance_monitor.py +289 -0
  51. contextsynapse/aiql/engine/postprocessing.py +365 -0
  52. contextsynapse/aiql/engine/ray_stage_executors.py +918 -0
  53. contextsynapse/aiql/engine/redis_cache.py +106 -0
  54. contextsynapse/aiql/engine/streaming_json.py +366 -0
  55. contextsynapse/aiql/engine/streaming_parquet.py +568 -0
  56. contextsynapse/aiql/engine/streaming_ray_chunking.py +419 -0
  57. contextsynapse/aiql/grammar/__init__.py +14 -0
  58. contextsynapse/aiql/grammar/aiql_grammar.py +1822 -0
  59. contextsynapse/aiql/grammar/base.py +71 -0
  60. contextsynapse/aiql/parser/__init__.py +46 -0
  61. contextsynapse/aiql/parser/aiql_parser.py +11330 -0
  62. contextsynapse/aiql/parser/base_parser.py +47 -0
  63. contextsynapse/api/__init__.py +5 -0
  64. contextsynapse/api/a2a_router.py +233 -0
  65. contextsynapse/api/admin_router.py +140 -0
  66. contextsynapse/api/agent_worker_router.py +978 -0
  67. contextsynapse/api/algorithms_router.py +317 -0
  68. contextsynapse/api/api.py +4144 -0
  69. contextsynapse/api/audit.py +204 -0
  70. contextsynapse/api/auth.py +441 -0
  71. contextsynapse/api/auth_router.py +537 -0
  72. contextsynapse/api/billing.py +261 -0
  73. contextsynapse/api/billing_router.py +275 -0
  74. contextsynapse/api/boundary_router.py +452 -0
  75. contextsynapse/api/cognition_router.py +116 -0
  76. contextsynapse/api/connector_router.py +237 -0
  77. contextsynapse/api/context_router.py +1619 -0
  78. contextsynapse/api/dashboard_router.py +11367 -0
  79. contextsynapse/api/errors.py +183 -0
  80. contextsynapse/api/events.py +88 -0
  81. contextsynapse/api/experiment_bootstrap.py +321 -0
  82. contextsynapse/api/experiment_orchestrator.py +1078 -0
  83. contextsynapse/api/experiment_router.py +2089 -0
  84. contextsynapse/api/feature_gate.py +89 -0
  85. contextsynapse/api/federation_router.py +108 -0
  86. contextsynapse/api/integrations.py +567 -0
  87. contextsynapse/api/integrations_router.py +461 -0
  88. contextsynapse/api/intelligence_router.py +1028 -0
  89. contextsynapse/api/metering.py +155 -0
  90. contextsynapse/api/metrics.py +112 -0
  91. contextsynapse/api/models.py +38 -0
  92. contextsynapse/api/monitoring_router.py +174 -0
  93. contextsynapse/api/pipeline_router.py +1437 -0
  94. contextsynapse/api/playground_conn.py +116 -0
  95. contextsynapse/api/projects_router.py +131 -0
  96. contextsynapse/api/queries_router.py +282 -0
  97. contextsynapse/api/quota.py +95 -0
  98. contextsynapse/api/rate_limit.py +64 -0
  99. contextsynapse/api/search_router.py +261 -0
  100. contextsynapse/api/shield_router.py +447 -0
  101. contextsynapse/api/tenant_guard.py +103 -0
  102. contextsynapse/api/tenant_quotas.py +231 -0
  103. contextsynapse/api/tenants.py +305 -0
  104. contextsynapse/api/users.py +646 -0
  105. contextsynapse/api/v1.py +167 -0
  106. contextsynapse/api/vertical_builder_router.py +387 -0
  107. contextsynapse/benchmark/__init__.py +15 -0
  108. contextsynapse/benchmark/__main__.py +64 -0
  109. contextsynapse/benchmark/agents.py +338 -0
  110. contextsynapse/benchmark/live_ab.py +338 -0
  111. contextsynapse/benchmark/reporter.py +149 -0
  112. contextsynapse/benchmark/runner.py +264 -0
  113. contextsynapse/benchmark/savings.py +266 -0
  114. contextsynapse/benchmark/token_counter.py +212 -0
  115. contextsynapse/benchmark/workloads.py +289 -0
  116. contextsynapse/buffer/__init__.py +154 -0
  117. contextsynapse/buffer/base.py +257 -0
  118. contextsynapse/buffer/duckdb_buffer.py +246 -0
  119. contextsynapse/buffer/file_buffer.py +229 -0
  120. contextsynapse/buffer/lmdb_buffer.py +286 -0
  121. contextsynapse/buffer/local_buffer.py +154 -0
  122. contextsynapse/buffer/redis_buffer.py +211 -0
  123. contextsynapse/buffer/sqlite_buffer.py +266 -0
  124. contextsynapse/cache.py +2 -0
  125. contextsynapse/cli/__init__.py +40 -0
  126. contextsynapse/cli/cli.py +475 -0
  127. contextsynapse/cli/config_cli.py +177 -0
  128. contextsynapse/cli/scaffold.py +465 -0
  129. contextsynapse/cognition/__init__.py +31 -0
  130. contextsynapse/cognition/derivation.py +55 -0
  131. contextsynapse/cognition/events.py +135 -0
  132. contextsynapse/cognition/feedback.py +52 -0
  133. contextsynapse/cognition/hallucination.py +90 -0
  134. contextsynapse/cognition/invalidation.py +70 -0
  135. contextsynapse/cognition/read_tracker.py +50 -0
  136. contextsynapse/collaboration.py +2 -0
  137. contextsynapse/comparison.py +2 -0
  138. contextsynapse/config/__init__.py +10 -0
  139. contextsynapse/config/connectors/amfi_nav.yaml +18 -0
  140. contextsynapse/config/connectors/auto_sector.yaml +20 -0
  141. contextsynapse/config/connectors/banking_sector.yaml +20 -0
  142. contextsynapse/config/connectors/energy_sector.yaml +20 -0
  143. contextsynapse/config/connectors/fmcg_sector.yaml +20 -0
  144. contextsynapse/config/connectors/nsdl_fpi.yaml +9 -0
  145. contextsynapse/config/connectors/nse_price.yaml +61 -0
  146. contextsynapse/config/connectors/pharma_sector.yaml +20 -0
  147. contextsynapse/config/connectors/rbi_dbie.yaml +12 -0
  148. contextsynapse/config/context_presets/ai_research.json +25 -0
  149. contextsynapse/config/context_presets/chatgpt_conversations.json +19 -0
  150. contextsynapse/config/context_presets/claude_conversations.json +19 -0
  151. contextsynapse/config/context_presets/legal_compliance.json +21 -0
  152. contextsynapse/config/context_presets/meeting_notes.json +21 -0
  153. contextsynapse/config/context_presets/slack_engineering.json +21 -0
  154. contextsynapse/config/context_presets/tech_climate.json +24 -0
  155. contextsynapse/config/context_presets/toi_politics.json +25 -0
  156. contextsynapse/config/database_config.py +453 -0
  157. contextsynapse/config/deployment.py +221 -0
  158. contextsynapse/config/gpt_action_schema.json +164 -0
  159. contextsynapse/config/indicators/india_macro.yaml +213 -0
  160. contextsynapse/config/logging_config.py +88 -0
  161. contextsynapse/config/mode_config.py +223 -0
  162. contextsynapse/config/schemas/api_spec.yaml +48 -0
  163. contextsynapse/config/schemas/architecture_doc.yaml +51 -0
  164. contextsynapse/config/schemas/chat_history.yaml +90 -0
  165. contextsynapse/config/schemas/chatgpt_conversation.yaml +101 -0
  166. contextsynapse/config/schemas/claude_conversation.yaml +148 -0
  167. contextsynapse/config/schemas/contract.yaml +48 -0
  168. contextsynapse/config/schemas/conversation.yaml +167 -0
  169. contextsynapse/config/schemas/database.yaml +74 -0
  170. contextsynapse/config/schemas/decision.yaml +72 -0
  171. contextsynapse/config/schemas/ecommerce.yaml +56 -0
  172. contextsynapse/config/schemas/education.yaml +42 -0
  173. contextsynapse/config/schemas/healthcare.yaml +133 -0
  174. contextsynapse/config/schemas/hr_document.yaml +48 -0
  175. contextsynapse/config/schemas/invoice.yaml +40 -0
  176. contextsynapse/config/schemas/knowledge_base.yaml +75 -0
  177. contextsynapse/config/schemas/legal.yaml +42 -0
  178. contextsynapse/config/schemas/marketing_content.yaml +48 -0
  179. contextsynapse/config/schemas/meeting_notes.yaml +51 -0
  180. contextsynapse/config/schemas/news_article.yaml +61 -0
  181. contextsynapse/config/schemas/real_estate.yaml +49 -0
  182. contextsynapse/config/schemas/requirements_doc.yaml +48 -0
  183. contextsynapse/config/schemas/research_paper.yaml +56 -0
  184. contextsynapse/config/schemas/resume.yaml +49 -0
  185. contextsynapse/config/schemas/retail.yaml +42 -0
  186. contextsynapse/config/schemas/rule_meta.yaml +173 -0
  187. contextsynapse/config/schemas/rules.yaml +72 -0
  188. contextsynapse/config/schemas/screener_meta.yaml +236 -0
  189. contextsynapse/config/schemas/sdlc.yaml +103 -0
  190. contextsynapse/config/schemas/session_graph.yaml +172 -0
  191. contextsynapse/config/schemas/support_ticket.yaml +48 -0
  192. contextsynapse/config/schemas/web.yaml +64 -0
  193. contextsynapse/config/sensors/earnings_calendar.yaml +52 -0
  194. contextsynapse/config/sensors/global_risk.yaml +53 -0
  195. contextsynapse/config/sensors/government_policy.yaml +54 -0
  196. contextsynapse/config/sensors/supply_chain.yaml +50 -0
  197. contextsynapse/config/sensors/templates/behavioral.yaml +56 -0
  198. contextsynapse/config/sensors/templates/equity_sentiment.yaml +48 -0
  199. contextsynapse/config/sensors/templates/global_macro.yaml +47 -0
  200. contextsynapse/config/sensors/templates/market_data.yaml +118 -0
  201. contextsynapse/config/sensors/templates/sector_technical.yaml +47 -0
  202. contextsynapse/config/sensors/weather.yaml +52 -0
  203. contextsynapse/config/storage_strategy.py +202 -0
  204. contextsynapse/config/templates/competitive_intel.yaml +39 -0
  205. contextsynapse/config/templates/geopolitical_risk.yaml +46 -0
  206. contextsynapse/config/templates/market_analysis.yaml +58 -0
  207. contextsynapse/connectors/__init__.py +15 -0
  208. contextsynapse/connectors/api_router.py +121 -0
  209. contextsynapse/connectors/base.py +357 -0
  210. contextsynapse/connectors/financial/audio.py +170 -0
  211. contextsynapse/connectors/financial/youtube.py +272 -0
  212. contextsynapse/connectors/github.py +164 -0
  213. contextsynapse/connectors/jira.py +120 -0
  214. contextsynapse/connectors/kafka_connector.py +124 -0
  215. contextsynapse/connectors/pipeline_connector.py +747 -0
  216. contextsynapse/connectors/registry.py +116 -0
  217. contextsynapse/connectors/scheduler.py +87 -0
  218. contextsynapse/connectors/universal.py +360 -0
  219. contextsynapse/context/__init__.py +96 -0
  220. contextsynapse/context/__main__.py +4 -0
  221. contextsynapse/context/acl.py +125 -0
  222. contextsynapse/context/agent_card.py +201 -0
  223. contextsynapse/context/agent_memory.py +814 -0
  224. contextsynapse/context/agents.py +472 -0
  225. contextsynapse/context/agents_redis.py +391 -0
  226. contextsynapse/context/assembled.py +204 -0
  227. contextsynapse/context/attribution.py +113 -0
  228. contextsynapse/context/blob.py +184 -0
  229. contextsynapse/context/boundaries.py +187 -0
  230. contextsynapse/context/boundary.py +507 -0
  231. contextsynapse/context/bundle.py +155 -0
  232. contextsynapse/context/cli.py +519 -0
  233. contextsynapse/context/collaboration.py +165 -0
  234. contextsynapse/context/compiler.py +604 -0
  235. contextsynapse/context/composite.py +536 -0
  236. contextsynapse/context/compression.py +147 -0
  237. contextsynapse/context/context_manager.py +888 -0
  238. contextsynapse/context/context_schema.py +90 -0
  239. contextsynapse/context/context_schema.yaml +105 -0
  240. contextsynapse/context/context_units.py +891 -0
  241. contextsynapse/context/conversation.py +488 -0
  242. contextsynapse/context/dedup.py +207 -0
  243. contextsynapse/context/document_processor.py +698 -0
  244. contextsynapse/context/embedding_hooks.py +338 -0
  245. contextsynapse/context/execution_schema.py +274 -0
  246. contextsynapse/context/fan_out.py +138 -0
  247. contextsynapse/context/frozen.py +80 -0
  248. contextsynapse/context/gravity.py +227 -0
  249. contextsynapse/context/hub.py +1401 -0
  250. contextsynapse/context/ingest.py +391 -0
  251. contextsynapse/context/injection.py +226 -0
  252. contextsynapse/context/interaction_graphifier.py +562 -0
  253. contextsynapse/context/layers.py +574 -0
  254. contextsynapse/context/projection.py +1195 -0
  255. contextsynapse/context/promotion.py +634 -0
  256. contextsynapse/context/proof.py +131 -0
  257. contextsynapse/context/propagation.py +577 -0
  258. contextsynapse/context/pubsub.py +135 -0
  259. contextsynapse/context/quality.py +1054 -0
  260. contextsynapse/context/quality_tracker.py +270 -0
  261. contextsynapse/context/scoping.py +370 -0
  262. contextsynapse/context/seed.py +992 -0
  263. contextsynapse/context/session.py +1142 -0
  264. contextsynapse/context/session_graph.py +593 -0
  265. contextsynapse/context/session_memory.py +309 -0
  266. contextsynapse/context/session_resolver.py +111 -0
  267. contextsynapse/context/skill_learning.py +237 -0
  268. contextsynapse/context/smart_budget.py +129 -0
  269. contextsynapse/context/source_policy.py +242 -0
  270. contextsynapse/context/store_factory.py +53 -0
  271. contextsynapse/context/sync.py +648 -0
  272. contextsynapse/context/token.py +236 -0
  273. contextsynapse/context/vector_integration.py +297 -0
  274. contextsynapse/context/webhooks.py +194 -0
  275. contextsynapse/context/working_memory.py +224 -0
  276. contextsynapse/core/__init__.py +18 -0
  277. contextsynapse/core/archiver.py +237 -0
  278. contextsynapse/core/change_stream.py +282 -0
  279. contextsynapse/core/checkpoint.py +310 -0
  280. contextsynapse/core/cloud_storage.py +387 -0
  281. contextsynapse/core/context_state.py +651 -0
  282. contextsynapse/core/cron.py +324 -0
  283. contextsynapse/core/datasource.py +643 -0
  284. contextsynapse/core/db.py +289 -0
  285. contextsynapse/core/federation.py +283 -0
  286. contextsynapse/core/graph_coordinator.py +201 -0
  287. contextsynapse/core/graph_intelligence.py +388 -0
  288. contextsynapse/core/graph_structures.py +106 -0
  289. contextsynapse/core/graph_sync.py +157 -0
  290. contextsynapse/core/hybrid_graph_storage.py +2998 -0
  291. contextsynapse/core/lifecycle.py +691 -0
  292. contextsynapse/core/multiworker.py +378 -0
  293. contextsynapse/core/pruning.py +312 -0
  294. contextsynapse/core/redis_registry.py +524 -0
  295. contextsynapse/core/registry.py +1043 -0
  296. contextsynapse/core/registry_factory.py +51 -0
  297. contextsynapse/core/registry_metadata.py +294 -0
  298. contextsynapse/core/replay.py +354 -0
  299. contextsynapse/core/replication.py +339 -0
  300. contextsynapse/core/time_travel.py +260 -0
  301. contextsynapse/core/write_behind.py +152 -0
  302. contextsynapse/core/write_context.py +39 -0
  303. contextsynapse/db/__init__.py +1 -0
  304. contextsynapse/db/postgres.py +267 -0
  305. contextsynapse/db/profile.py +236 -0
  306. contextsynapse/db/rules.py +328 -0
  307. contextsynapse/demo/__init__.py +0 -0
  308. contextsynapse/demo/report.py +147 -0
  309. contextsynapse/demo/runner.py +520 -0
  310. contextsynapse/demo/scenario.py +165 -0
  311. contextsynapse/demo/session_demo.py +280 -0
  312. contextsynapse/demo/session_report.py +143 -0
  313. contextsynapse/demo/universal_runner.py +257 -0
  314. contextsynapse/engine/__init__.py +15 -0
  315. contextsynapse/engine/core.py +555 -0
  316. contextsynapse/extraction/__init__.py +69 -0
  317. contextsynapse/extraction/cli.py +274 -0
  318. contextsynapse/extraction/config.py +107 -0
  319. contextsynapse/extraction/examples/example_usage.py +118 -0
  320. contextsynapse/extraction/examples/sample_manifest.json +53 -0
  321. contextsynapse/extraction/examples/sample_normalized.json +185 -0
  322. contextsynapse/extraction/extract_engine.py +440 -0
  323. contextsynapse/extraction/extractors/__init__.py +37 -0
  324. contextsynapse/extraction/extractors/chat_export_extractor.py +189 -0
  325. contextsynapse/extraction/extractors/csv_extractor.py +83 -0
  326. contextsynapse/extraction/extractors/docx_extractor.py +129 -0
  327. contextsynapse/extraction/extractors/excel_extractor.py +152 -0
  328. contextsynapse/extraction/extractors/html_extractor.py +146 -0
  329. contextsynapse/extraction/extractors/pdf_extractor.py +635 -0
  330. contextsynapse/extraction/extractors/pdf_extractor_layoutparser.py +408 -0
  331. contextsynapse/extraction/extractors/register_extractors.py +63 -0
  332. contextsynapse/extraction/extractors/text_extractor.py +65 -0
  333. contextsynapse/extraction/extractors/txt_extractor.py +120 -0
  334. contextsynapse/extraction/extractors/website_extractor.py +246 -0
  335. contextsynapse/extraction/fact_extractor.py +236 -0
  336. contextsynapse/extraction/file_manager.py +353 -0
  337. contextsynapse/extraction/hierarchy.py +239 -0
  338. contextsynapse/extraction/id_generator.py +450 -0
  339. contextsynapse/extraction/layout_detector.py +409 -0
  340. contextsynapse/extraction/llm_entity_extractor.py +301 -0
  341. contextsynapse/extraction/normalized_store.py +480 -0
  342. contextsynapse/extraction/normalizer.py +594 -0
  343. contextsynapse/extraction/parquet_writer.py +598 -0
  344. contextsynapse/extraction/post_processors/__init__.py +40 -0
  345. contextsynapse/extraction/post_processors/column_reorganizer.py +507 -0
  346. contextsynapse/extraction/ray_normalization.py +233 -0
  347. contextsynapse/extraction/ray_runner.py +207 -0
  348. contextsynapse/extraction/redis_schema_registry.py +222 -0
  349. contextsynapse/extraction/registry.py +204 -0
  350. contextsynapse/extraction/schema_loader.py +663 -0
  351. contextsynapse/extraction/schema_registry.py +230 -0
  352. contextsynapse/extraction/semantic_normalizer.py +245 -0
  353. contextsynapse/extraction/version_manager.py +314 -0
  354. contextsynapse/gateway/__init__.py +24 -0
  355. contextsynapse/gateway/action_emitter.py +212 -0
  356. contextsynapse/gateway/cost_tracker.py +508 -0
  357. contextsynapse/gateway/gateway.py +377 -0
  358. contextsynapse/gateway/policy.py +303 -0
  359. contextsynapse/governance/__init__.py +13 -0
  360. contextsynapse/governance/layer.py +513 -0
  361. contextsynapse/ingestion/__init__.py +15 -0
  362. contextsynapse/ingestion/amplifier.py +288 -0
  363. contextsynapse/ingestion/async_ingest.py +463 -0
  364. contextsynapse/ingestion/chunker.py +303 -0
  365. contextsynapse/ingestion/chunking.py +58 -0
  366. contextsynapse/ingestion/cleaner.py +190 -0
  367. contextsynapse/ingestion/cleanse.py +252 -0
  368. contextsynapse/ingestion/connectors/__init__.py +38 -0
  369. contextsynapse/ingestion/connectors/base.py +300 -0
  370. contextsynapse/ingestion/connectors/chatgpt_memory.py +247 -0
  371. contextsynapse/ingestion/connectors/claude_memory.py +274 -0
  372. contextsynapse/ingestion/connectors/sap_ingestor.py +208 -0
  373. contextsynapse/ingestion/content_detector.py +80 -0
  374. contextsynapse/ingestion/content_monitor.py +502 -0
  375. contextsynapse/ingestion/dedup.py +106 -0
  376. contextsynapse/ingestion/feed.py +640 -0
  377. contextsynapse/ingestion/filters.py +461 -0
  378. contextsynapse/ingestion/graph_builder.py +705 -0
  379. contextsynapse/ingestion/input_classifier.py +307 -0
  380. contextsynapse/ingestion/job_manager.py +571 -0
  381. contextsynapse/ingestion/llm_extractor.py +610 -0
  382. contextsynapse/ingestion/orchestrator.py +116 -0
  383. contextsynapse/ingestion/parsers/__init__.py +19 -0
  384. contextsynapse/ingestion/parsers/graph_parser_registry.py +103 -0
  385. contextsynapse/ingestion/parsers/graphml_parser.py +141 -0
  386. contextsynapse/ingestion/parsers/jsonld_parser.py +171 -0
  387. contextsynapse/ingestion/parsers/rdf_parser.py +150 -0
  388. contextsynapse/ingestion/pipeline_context.py +228 -0
  389. contextsynapse/ingestion/pipeline_store.py +1195 -0
  390. contextsynapse/ingestion/queue.py +133 -0
  391. contextsynapse/ingestion/reprocess.py +179 -0
  392. contextsynapse/ingestion/scenario_router.py +224 -0
  393. contextsynapse/ingestion/schema_extractor.py +759 -0
  394. contextsynapse/ingestion/schema_validator.py +274 -0
  395. contextsynapse/ingestion/sdlc_ingest.py +87 -0
  396. contextsynapse/ingestion/smart_ingest.py +2451 -0
  397. contextsynapse/ingestion/source_connector.py +171 -0
  398. contextsynapse/ingestion/stage_executor.py +2095 -0
  399. contextsynapse/ingestion/stages/__init__.py +9 -0
  400. contextsynapse/ingestion/stages/tabular_to_graph.py +289 -0
  401. contextsynapse/ingestion/strategies.py +386 -0
  402. contextsynapse/ingestion/universal/__init__.py +4 -0
  403. contextsynapse/ingestion/universal/_operator_registry.py +59 -0
  404. contextsynapse/ingestion/universal/ingest_content.py +32 -0
  405. contextsynapse/ingestion/universal/operators/__init__.py +1 -0
  406. contextsynapse/ingestion/universal/operators/base.py +19 -0
  407. contextsynapse/ingestion/universal/operators/build_edges.py +75 -0
  408. contextsynapse/ingestion/universal/operators/cluster_topics.py +397 -0
  409. contextsynapse/ingestion/universal/operators/deduplicate.py +45 -0
  410. contextsynapse/ingestion/universal/operators/detect_signals.py +90 -0
  411. contextsynapse/ingestion/universal/operators/embed.py +80 -0
  412. contextsynapse/ingestion/universal/operators/extract_entities.py +253 -0
  413. contextsynapse/ingestion/universal/operators/extract_preferences.py +117 -0
  414. contextsynapse/ingestion/universal/operators/filter_content.py +69 -0
  415. contextsynapse/ingestion/universal/operators/index_bm25.py +52 -0
  416. contextsynapse/ingestion/universal/operators/infer_domains.py +69 -0
  417. contextsynapse/ingestion/universal/operators/link_cross_reference.py +210 -0
  418. contextsynapse/ingestion/universal/operators/parse_records.py +66 -0
  419. contextsynapse/ingestion/universal/operators/resolve_entities.py +261 -0
  420. contextsynapse/ingestion/universal/operators/scanners/__init__.py +16 -0
  421. contextsynapse/ingestion/universal/operators/scanners/edges.py +173 -0
  422. contextsynapse/ingestion/universal/operators/scanners/git.py +187 -0
  423. contextsynapse/ingestion/universal/operators/scanners/github.py +135 -0
  424. contextsynapse/ingestion/universal/operators/scanners/github_api.py +509 -0
  425. contextsynapse/ingestion/universal/operators/scanners/helpers.py +67 -0
  426. contextsynapse/ingestion/universal/operators/scanners/jira.py +65 -0
  427. contextsynapse/ingestion/universal/operators/scanners/llm_enrichment.py +82 -0
  428. contextsynapse/ingestion/universal/operators/scanners/repo.py +149 -0
  429. contextsynapse/ingestion/universal/operators/sdlc_scan.py +213 -0
  430. contextsynapse/ingestion/universal/operators/spec_scanner.py +149 -0
  431. contextsynapse/ingestion/universal/operators/store_documents.py +72 -0
  432. contextsynapse/ingestion/universal/operators/synthesize_cu.py +180 -0
  433. contextsynapse/ingestion/universal/operators/validate_gate.py +65 -0
  434. contextsynapse/ingestion/universal/parsers/__init__.py +5 -0
  435. contextsynapse/ingestion/universal/parsers/record_parser.py +203 -0
  436. contextsynapse/ingestion/universal/parsers/turn_parser.py +329 -0
  437. contextsynapse/ingestion/universal/shared_signals.py +161 -0
  438. contextsynapse/ingestion/universal/stage_executor.py +219 -0
  439. contextsynapse/ingestion/web_crawler.py +265 -0
  440. contextsynapse/intelligence/__init__.py +53 -0
  441. contextsynapse/intelligence/alerts.py +198 -0
  442. contextsynapse/intelligence/collector.py +655 -0
  443. contextsynapse/intelligence/comparison.py +198 -0
  444. contextsynapse/intelligence/config.py +56 -0
  445. contextsynapse/intelligence/conflict_detector.py +182 -0
  446. contextsynapse/intelligence/context_gaps.py +192 -0
  447. contextsynapse/intelligence/context_radar.py +135 -0
  448. contextsynapse/intelligence/correlation_engine.py +416 -0
  449. contextsynapse/intelligence/correlation_template.py +120 -0
  450. contextsynapse/intelligence/dedup.py +296 -0
  451. contextsynapse/intelligence/event_bus.py +91 -0
  452. contextsynapse/intelligence/feedback_loop.py +101 -0
  453. contextsynapse/intelligence/freshness.py +307 -0
  454. contextsynapse/intelligence/fundamental_feed.py +551 -0
  455. contextsynapse/intelligence/geo_data.py +170 -0
  456. contextsynapse/intelligence/geo_reference.py +112 -0
  457. contextsynapse/intelligence/graph_fusion.py +465 -0
  458. contextsynapse/intelligence/heuristics.py +176 -0
  459. contextsynapse/intelligence/impact_tracker.py +336 -0
  460. contextsynapse/intelligence/macro_indicators.py +319 -0
  461. contextsynapse/intelligence/ocr.py +104 -0
  462. contextsynapse/intelligence/persistence.py +267 -0
  463. contextsynapse/intelligence/pipeline_health.py +130 -0
  464. contextsynapse/intelligence/price_feed.py +221 -0
  465. contextsynapse/intelligence/rate_limiter.py +130 -0
  466. contextsynapse/intelligence/reactive.py +411 -0
  467. contextsynapse/intelligence/runtime_context.py +390 -0
  468. contextsynapse/intelligence/sentiment_decay.py +426 -0
  469. contextsynapse/intelligence/session.py +277 -0
  470. contextsynapse/intelligence/signal_hierarchy.py +438 -0
  471. contextsynapse/intelligence/signals.py +185 -0
  472. contextsynapse/intelligence/source_watcher.py +132 -0
  473. contextsynapse/intelligence/table_enricher.py +256 -0
  474. contextsynapse/intelligence/tagger.py +319 -0
  475. contextsynapse/intelligence/timeseries.py +209 -0
  476. contextsynapse/intelligence/tracker.py +318 -0
  477. contextsynapse/intelligence/watchdog.py +392 -0
  478. contextsynapse/intelligence/watchdog_manager.py +263 -0
  479. contextsynapse/intelligence/webhook_receiver.py +165 -0
  480. contextsynapse/intelligence/weight_learner.py +234 -0
  481. contextsynapse/llm/__init__.py +8 -0
  482. contextsynapse/llm/client.py +348 -0
  483. contextsynapse/llm/rate_limiter.py +166 -0
  484. contextsynapse/logging_config.py +2 -0
  485. contextsynapse/marketplace/__init__.py +7 -0
  486. contextsynapse/marketplace/registry.py +173 -0
  487. contextsynapse/mcp/__init__.py +11 -0
  488. contextsynapse/mcp/__main__.py +4 -0
  489. contextsynapse/mcp/auth_middleware.py +212 -0
  490. contextsynapse/mcp/connection_pool.py +128 -0
  491. contextsynapse/mcp/server.py +986 -0
  492. contextsynapse/mcp/session_context.py +67 -0
  493. contextsynapse/memory/__init__.py +34 -0
  494. contextsynapse/memory/engine.py +475 -0
  495. contextsynapse/memory/temporal.py +327 -0
  496. contextsynapse/metadata/__init__.py +12 -0
  497. contextsynapse/metadata/metadata_db.py +272 -0
  498. contextsynapse/metadata/metadata_query_tool.py +300 -0
  499. contextsynapse/metadata/metadata_tracker.py +425 -0
  500. contextsynapse/models/__init__.py +27 -0
  501. contextsynapse/models/embedding_service.py +360 -0
  502. contextsynapse/models/model_registry.py +228 -0
  503. contextsynapse/multiworker.py +2 -0
  504. contextsynapse/pipelines/__init__.py +1 -0
  505. contextsynapse/pipelines/executor.py +851 -0
  506. contextsynapse/pipelines/filters.py +189 -0
  507. contextsynapse/pipelines/models.py +176 -0
  508. contextsynapse/pipelines/scheduler.py +268 -0
  509. contextsynapse/playground/__init__.py +1 -0
  510. contextsynapse/playground/templates.py +85 -0
  511. contextsynapse/plugins/__init__.py +43 -0
  512. contextsynapse/plugins/api.py +66 -0
  513. contextsynapse/plugins/base.py +350 -0
  514. contextsynapse/plugins/domain_interface.py +508 -0
  515. contextsynapse/plugins/domain_loader.py +241 -0
  516. contextsynapse/plugins/loader.py +148 -0
  517. contextsynapse/plugins/registry.py +265 -0
  518. contextsynapse/project/__init__.py +11 -0
  519. contextsynapse/project/code_context.py +807 -0
  520. contextsynapse/project/connectors/__init__.py +4 -0
  521. contextsynapse/project/connectors/github_connector.py +113 -0
  522. contextsynapse/project/connectors/jira_connector.py +160 -0
  523. contextsynapse/project/graph.py +1023 -0
  524. contextsynapse/project/project_context.py +545 -0
  525. contextsynapse/project/scanner_registry.py +61 -0
  526. contextsynapse/project/schema_manager.py +322 -0
  527. contextsynapse/project/schemas/knowledge.yaml +59 -0
  528. contextsynapse/project/schemas/sdlc.yaml +369 -0
  529. contextsynapse/project/sdlc_schema.py +172 -0
  530. contextsynapse/project/spec_parser.py +85 -0
  531. contextsynapse/project/task_lock.py +181 -0
  532. contextsynapse/project/task_stream.py +203 -0
  533. contextsynapse/project/team_tools.py +537 -0
  534. contextsynapse/py.typed +0 -0
  535. contextsynapse/realtime/__init__.py +6 -0
  536. contextsynapse/realtime/collaboration.py +147 -0
  537. contextsynapse/rules/__init__.py +10 -0
  538. contextsynapse/rules/engine.py +116 -0
  539. contextsynapse/rules/evaluators/__init__.py +6 -0
  540. contextsynapse/rules/evaluators/aggregate.py +205 -0
  541. contextsynapse/rules/evaluators/custom.py +170 -0
  542. contextsynapse/rules/evaluators/pre_action.py +125 -0
  543. contextsynapse/rules/evaluators/temporal.py +129 -0
  544. contextsynapse/rules/loader.py +52 -0
  545. contextsynapse/rules/models.py +124 -0
  546. contextsynapse/rules/universal.py +393 -0
  547. contextsynapse/scheduler.py +229 -0
  548. contextsynapse/schema/__init__.py +42 -0
  549. contextsynapse/schema/compiler.py +252 -0
  550. contextsynapse/schema/composer.py +158 -0
  551. contextsynapse/schema/dedup.py +73 -0
  552. contextsynapse/schema/derivation.py +172 -0
  553. contextsynapse/schema/sdl.py +326 -0
  554. contextsynapse/schema/validation_gate.py +124 -0
  555. contextsynapse/sdk/__init__.py +63 -0
  556. contextsynapse/sdk/agent.py +214 -0
  557. contextsynapse/sdk/client.py +269 -0
  558. contextsynapse/sdk/exceptions.py +23 -0
  559. contextsynapse/sdk/models.py +78 -0
  560. contextsynapse/sdk/session.py +408 -0
  561. contextsynapse/sdk/tracked.py +218 -0
  562. contextsynapse/sdk/worker.py +266 -0
  563. contextsynapse/sdk/workspace_client.py +98 -0
  564. contextsynapse/sdk/ws.py +41 -0
  565. contextsynapse/search/__init__.py +20 -0
  566. contextsynapse/search/embedding_cache.py +140 -0
  567. contextsynapse/search/enhanced_search.py +152 -0
  568. contextsynapse/search/fulltext.py +130 -0
  569. contextsynapse/search/graph_search.py +634 -0
  570. contextsynapse/search/lmdb_index.py +1218 -0
  571. contextsynapse/search/nl_to_aiql.py +199 -0
  572. contextsynapse/search/rag.py +1346 -0
  573. contextsynapse/search/rag_cache.py +167 -0
  574. contextsynapse/search/reasoning_chain.py +231 -0
  575. contextsynapse/search/redis_search.py +207 -0
  576. contextsynapse/search/retrieval_quality.py +314 -0
  577. contextsynapse/search/semantic_query.py +455 -0
  578. contextsynapse/search/text_resolver.py +159 -0
  579. contextsynapse/search/whoosh_search.py +373 -0
  580. contextsynapse/security/__init__.py +17 -0
  581. contextsynapse/security/audit.py +297 -0
  582. contextsynapse/security/audit_logger.py +97 -0
  583. contextsynapse/security/audit_trail.py +270 -0
  584. contextsynapse/security/auth_unified.py +284 -0
  585. contextsynapse/security/auto_tagger.py +230 -0
  586. contextsynapse/security/data_security.py +218 -0
  587. contextsynapse/security/encryption.py +207 -0
  588. contextsynapse/security/identity.py +447 -0
  589. contextsynapse/security/jwt_identity.py +59 -0
  590. contextsynapse/security/middleware.py +336 -0
  591. contextsynapse/security/pii.py +233 -0
  592. contextsynapse/security/rbac.py +96 -0
  593. contextsynapse/security/rls.py +205 -0
  594. contextsynapse/security/sanitize.py +233 -0
  595. contextsynapse/security/scoped_encryption.py +131 -0
  596. contextsynapse/security/tenant.py +401 -0
  597. contextsynapse/shield/__init__.py +21 -0
  598. contextsynapse/shield/anomaly.py +56 -0
  599. contextsynapse/shield/permissions.py +99 -0
  600. contextsynapse/shield/profile.py +103 -0
  601. contextsynapse/shield/shield.py +108 -0
  602. contextsynapse/shield/trust_engine.py +69 -0
  603. contextsynapse/skills/__init__.py +14 -0
  604. contextsynapse/skills/engine.py +233 -0
  605. contextsynapse/skills/loader.py +59 -0
  606. contextsynapse/skills/models.py +139 -0
  607. contextsynapse/storage/__init__.py +7 -0
  608. contextsynapse/storage/cache.py +208 -0
  609. contextsynapse/storage/columnar_store_v2.py +178 -0
  610. contextsynapse/storage/csr_graph_storage.py +587 -0
  611. contextsynapse/storage/csr_store.py +388 -0
  612. contextsynapse/storage/document_store.py +491 -0
  613. contextsynapse/storage/hnsw_index.py +294 -0
  614. contextsynapse/storage/hybrid_store_v2.py +278 -0
  615. contextsynapse/storage/lmdb_graph_storage.py +366 -0
  616. contextsynapse/storage/migration.py +279 -0
  617. contextsynapse/storage/namespace_store.py +346 -0
  618. contextsynapse/storage/pure_graph_storage.py +185 -0
  619. contextsynapse/storage/redis_graph_adapter.py +186 -0
  620. contextsynapse/storage/redis_graph_storage.py +375 -0
  621. contextsynapse/storage/router/__init__.py +29 -0
  622. contextsynapse/storage/router/duckdb_store.py +398 -0
  623. contextsynapse/storage/router/factory.py +83 -0
  624. contextsynapse/storage/router/interface.py +125 -0
  625. contextsynapse/storage/router/postgres_store.py +339 -0
  626. contextsynapse/storage/router/resolver.py +192 -0
  627. contextsynapse/storage/router/router.py +300 -0
  628. contextsynapse/storage/single_file_storage.py +391 -0
  629. contextsynapse/storage/storage_manager.py +214 -0
  630. contextsynapse/storage/storage_query_optimizer.py +314 -0
  631. contextsynapse/storage/wal.py +359 -0
  632. contextsynapse/temporal/__init__.py +0 -0
  633. contextsynapse/temporal/storage.py +106 -0
  634. contextsynapse/tests/__init__.py +11 -0
  635. contextsynapse/tests/regression/__init__.py +1 -0
  636. contextsynapse/tests/regression/add_metadata.py +395 -0
  637. contextsynapse/tests/regression/add_skip_flags.py +85 -0
  638. contextsynapse/tests/regression/cases/test_basic_create_node.yaml +20 -0
  639. contextsynapse/tests/regression/cases/test_basic_select.yaml +21 -0
  640. contextsynapse/tests/regression/cases/test_blockchain_audit_trail.yaml +37 -0
  641. contextsynapse/tests/regression/cases/test_blockchain_audit_trail_filtered.yaml +39 -0
  642. contextsynapse/tests/regression/cases/test_blockchain_blocks_range.yaml +39 -0
  643. contextsynapse/tests/regression/cases/test_blockchain_get_block.yaml +35 -0
  644. contextsynapse/tests/regression/cases/test_blockchain_latest_block.yaml +35 -0
  645. contextsynapse/tests/regression/cases/test_blockchain_length.yaml +37 -0
  646. contextsynapse/tests/regression/cases/test_blockchain_merkle_root.yaml +37 -0
  647. contextsynapse/tests/regression/cases/test_blockchain_verify.yaml +35 -0
  648. contextsynapse/tests/regression/cases/test_blockchain_verify_block.yaml +35 -0
  649. contextsynapse/tests/regression/cases/test_create_edge.yaml +28 -0
  650. contextsynapse/tests/regression/cases/test_delete_edge_basic.yaml +29 -0
  651. contextsynapse/tests/regression/cases/test_delete_node_basic.yaml +29 -0
  652. contextsynapse/tests/regression/cases/test_evaluation_mrr.yaml +40 -0
  653. contextsynapse/tests/regression/cases/test_evaluation_ndcg.yaml +40 -0
  654. contextsynapse/tests/regression/cases/test_evaluation_precision.yaml +40 -0
  655. contextsynapse/tests/regression/cases/test_evaluation_recall.yaml +40 -0
  656. contextsynapse/tests/regression/cases/test_group_by_basic.yaml +56 -0
  657. contextsynapse/tests/regression/cases/test_match_node_basic.yaml +29 -0
  658. contextsynapse/tests/regression/cases/test_match_node_with_order_limit.yaml +31 -0
  659. contextsynapse/tests/regression/cases/test_namespace_switch.yaml +18 -0
  660. contextsynapse/tests/regression/cases/test_pipeline_cascaded.yaml +61 -0
  661. contextsynapse/tests/regression/cases/test_pipeline_fixed_size_chunking.yaml +48 -0
  662. contextsynapse/tests/regression/cases/test_pipeline_gpt4_chunking.yaml +54 -0
  663. contextsynapse/tests/regression/cases/test_pipeline_hybrid_indexing.yaml +57 -0
  664. contextsynapse/tests/regression/cases/test_pipeline_large_embedding.yaml +52 -0
  665. contextsynapse/tests/regression/cases/test_pipeline_paragraph_chunking.yaml +49 -0
  666. contextsynapse/tests/regression/cases/test_pipeline_recursive_chunking.yaml +47 -0
  667. contextsynapse/tests/regression/cases/test_pipeline_run_pipeline.yaml +56 -0
  668. contextsynapse/tests/regression/cases/test_pipeline_section_based_chunking.yaml +53 -0
  669. contextsynapse/tests/regression/cases/test_pipeline_semantic_chunking.yaml +51 -0
  670. contextsynapse/tests/regression/cases/test_pipeline_with_entity_extraction.yaml +57 -0
  671. contextsynapse/tests/regression/cases/test_pipeline_with_relationships.yaml +62 -0
  672. contextsynapse/tests/regression/cases/test_rag_evaluation.yaml +38 -0
  673. contextsynapse/tests/regression/cases/test_rag_graph_bfs_with_eval.yaml +48 -0
  674. contextsynapse/tests/regression/cases/test_rag_graph_centrality.yaml +40 -0
  675. contextsynapse/tests/regression/cases/test_rag_graph_centrality_eval.yaml +35 -0
  676. contextsynapse/tests/regression/cases/test_rag_graph_community.yaml +40 -0
  677. contextsynapse/tests/regression/cases/test_rag_graph_community_eval.yaml +35 -0
  678. contextsynapse/tests/regression/cases/test_rag_graph_dfs_with_eval.yaml +35 -0
  679. contextsynapse/tests/regression/cases/test_rag_graph_multi_hop_eval.yaml +35 -0
  680. contextsynapse/tests/regression/cases/test_rag_graph_path.yaml +40 -0
  681. contextsynapse/tests/regression/cases/test_rag_graph_search_basic.yaml +38 -0
  682. contextsynapse/tests/regression/cases/test_rag_graph_search_bfs.yaml +52 -0
  683. contextsynapse/tests/regression/cases/test_rag_graph_search_dfs.yaml +41 -0
  684. contextsynapse/tests/regression/cases/test_rag_graph_search_multi_hop.yaml +43 -0
  685. contextsynapse/tests/regression/cases/test_rag_graph_search_shortest_path.yaml +41 -0
  686. contextsynapse/tests/regression/cases/test_rag_graph_search_weighted.yaml +43 -0
  687. contextsynapse/tests/regression/cases/test_rag_graph_shortest_path_eval.yaml +35 -0
  688. contextsynapse/tests/regression/cases/test_rag_graph_traversal.yaml +30 -0
  689. contextsynapse/tests/regression/cases/test_rag_graph_weighted_eval.yaml +35 -0
  690. contextsynapse/tests/regression/cases/test_rag_hybrid_search.yaml +33 -0
  691. contextsynapse/tests/regression/cases/test_rag_hybrid_with_prompt_embedding.yaml +44 -0
  692. contextsynapse/tests/regression/cases/test_rag_keyword_search.yaml +32 -0
  693. contextsynapse/tests/regression/cases/test_rag_multi_hop_graph.yaml +32 -0
  694. contextsynapse/tests/regression/cases/test_rag_path_based.yaml +34 -0
  695. contextsynapse/tests/regression/cases/test_rag_profile_based.yaml +32 -0
  696. contextsynapse/tests/regression/cases/test_rag_vector_search.yaml +32 -0
  697. contextsynapse/tests/regression/cases/test_rag_weighted_graph.yaml +34 -0
  698. contextsynapse/tests/regression/cases/test_rag_weighted_hybrid.yaml +31 -0
  699. contextsynapse/tests/regression/cases/test_semantic_boundary_chunking.yaml +57 -0
  700. contextsynapse/tests/regression/cases/test_semantic_boundary_detection.yaml +33 -0
  701. contextsynapse/tests/regression/cases/test_semantic_hash_function.yaml +30 -0
  702. contextsynapse/tests/regression/cases/test_semantic_hash_with_model.yaml +31 -0
  703. contextsynapse/tests/regression/cases/test_tcs_ingestion_fixed_size.yaml +44 -0
  704. contextsynapse/tests/regression/cases/test_tcs_ingestion_recursive.yaml +46 -0
  705. contextsynapse/tests/regression/cases/test_tcs_ingestion_section.yaml +46 -0
  706. contextsynapse/tests/regression/cases/test_tcs_ingestion_semantic.yaml +44 -0
  707. contextsynapse/tests/regression/cases/test_time_travel_as_of.yaml +35 -0
  708. contextsynapse/tests/regression/cases/test_time_travel_at_commit.yaml +33 -0
  709. contextsynapse/tests/regression/cases/test_time_travel_at_timestamp.yaml +33 -0
  710. contextsynapse/tests/regression/cases/test_time_travel_for_system_time_all.yaml +37 -0
  711. contextsynapse/tests/regression/cases/test_time_travel_versions_between.yaml +39 -0
  712. contextsynapse/tests/regression/cases/test_traverse_basic.yaml +31 -0
  713. contextsynapse/tests/regression/cases/test_traverse_max_depth.yaml +34 -0
  714. contextsynapse/tests/regression/cases/test_update_edge_basic.yaml +29 -0
  715. contextsynapse/tests/regression/evaluation_metrics.py +227 -0
  716. contextsynapse/tests/regression/regression_runner.py +1081 -0
  717. contextsynapse/tests/regression/setup_regression_data.py +212 -0
  718. contextsynapse/tests/regression/test_basic_only.py +84 -0
  719. contextsynapse/tests/regression/test_namespace_creation.py +29 -0
  720. contextsynapse/tests/regression/test_single_fix.py +61 -0
  721. contextsynapse/tests/regression/test_single_query.py +20 -0
  722. contextsynapse/tests/regression/validate_test_syntax.py +179 -0
  723. contextsynapse/tests/test_advanced_features.py +347 -0
  724. contextsynapse/tests/test_aiql_summary.py +283 -0
  725. contextsynapse/tests/test_all_aiql_queries.py +948 -0
  726. contextsynapse/tests/test_all_create_queries.py +213 -0
  727. contextsynapse/tests/test_api_vs_native.py +280 -0
  728. contextsynapse/tests/test_chunking_dedup.py +199 -0
  729. contextsynapse/tests/test_connectors.py +171 -0
  730. contextsynapse/tests/test_edges_and_match.py +202 -0
  731. contextsynapse/tests/test_extraction_pipeline_integration.py +581 -0
  732. contextsynapse/tests/test_name_property.py +224 -0
  733. contextsynapse/tests/test_node_edge_metadata.py +250 -0
  734. contextsynapse/tests/test_playground_conn.py +114 -0
  735. contextsynapse/tests/test_query_cache.py +213 -0
  736. contextsynapse/tests/test_source_policy_auth.py +348 -0
  737. contextsynapse/tests/test_temporal_and_domain.py +224 -0
  738. contextsynapse/tests/test_time_travel_e2e.py +571 -0
  739. contextsynapse/tests/test_traversal_and_aggregation.py +275 -0
  740. contextsynapse/tools/__init__.py +36 -0
  741. contextsynapse/tools/a2a_tools.py +106 -0
  742. contextsynapse/tools/cognition_tools.py +118 -0
  743. contextsynapse/tools/context.py +905 -0
  744. contextsynapse/tools/formatting.py +92 -0
  745. contextsynapse/tools/gateway_tools.py +186 -0
  746. contextsynapse/tools/graph.py +1700 -0
  747. contextsynapse/tools/intelligence.py +235 -0
  748. contextsynapse/tools/memory.py +118 -0
  749. contextsynapse/tools/registry.py +373 -0
  750. contextsynapse/tools/shield_tools.py +159 -0
  751. contextsynapse/tools/tasks.py +341 -0
  752. contextsynapse/tools/workspace_tools.py +124 -0
  753. contextsynapse/utils/__init__.py +15 -0
  754. contextsynapse/utils/config_loader.py +198 -0
  755. contextsynapse/utils/env_loader.py +156 -0
  756. contextsynapse/utils/file_utils.py +288 -0
  757. contextsynapse/utils/logging_optimizer.py +119 -0
  758. contextsynapse/vector/__init__.py +30 -0
  759. contextsynapse/vector/chroma_vector_store.py +152 -0
  760. contextsynapse/vector/custom_vector_store.py +537 -0
  761. contextsynapse/vector/faiss_vector_store.py +296 -0
  762. contextsynapse/vector/qdrant_vector_store.py +163 -0
  763. contextsynapse/vector/vector_db.py +953 -0
  764. contextsynapse/vector/vector_db_manager.py +197 -0
  765. contextsynapse/workspace/__init__.py +27 -0
  766. contextsynapse/workspace/base.py +94 -0
  767. contextsynapse/workspace/git.py +228 -0
  768. contextsynapse/workspace/github.py +78 -0
  769. contextsynapse/workspace/local.py +73 -0
  770. contextsynapse/workspace/tools.py +222 -0
  771. contextsynapse-1.0.0.dist-info/METADATA +847 -0
  772. contextsynapse-1.0.0.dist-info/RECORD +776 -0
  773. contextsynapse-1.0.0.dist-info/WHEEL +5 -0
  774. contextsynapse-1.0.0.dist-info/entry_points.txt +4 -0
  775. contextsynapse-1.0.0.dist-info/licenses/LICENSE +200 -0
  776. contextsynapse-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,646 @@
1
+ """
2
+ User Registry
3
+ =============
4
+ Self-service user accounts for the AIContextDB SaaS layer.
5
+
6
+ Each user belongs to one or more tenants (organisations) via the
7
+ ``user_tenants`` join table. On signup a personal tenant is auto-created
8
+ and the user is set as its owner.
9
+
10
+ Usage::
11
+
12
+ registry = UserRegistry()
13
+ user, jwt = registry.signup("alice@example.com", "s3cret", "Alice")
14
+ user = registry.authenticate("alice@example.com", "s3cret")
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import hashlib
20
+ import json
21
+ import logging
22
+ import re
23
+ import secrets
24
+ from dataclasses import dataclass, field, asdict
25
+ from datetime import datetime, timezone
26
+ from pathlib import Path
27
+ from typing import Any, Dict, List, Optional, Tuple
28
+
29
+ from contextsynapse.core.db import (
30
+ IS_POSTGRES, PH,
31
+ connect, dict_cursor, run_ddl,
32
+ integrity_error, column_exists, row_to_dict, serial_pk,
33
+ )
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+ # ---------------------------------------------------------------------------
38
+ # bcrypt password hashing (direct, no passlib needed)
39
+ # ---------------------------------------------------------------------------
40
+ try:
41
+ import bcrypt as _bcrypt
42
+ except ImportError:
43
+ _bcrypt = None
44
+ logger.warning("bcrypt not installed — falling back to SHA-256 password hashing")
45
+
46
+
47
+ def _hash_password(password: str) -> str:
48
+ if _bcrypt:
49
+ return _bcrypt.hashpw(password.encode("utf-8")[:72], _bcrypt.gensalt()).decode("utf-8")
50
+ return hashlib.sha256(password.encode()).hexdigest()
51
+
52
+
53
+ def _verify_password(password: str, hashed: str) -> bool:
54
+ if _bcrypt and hashed.startswith("$2"):
55
+ return _bcrypt.checkpw(password.encode("utf-8")[:72], hashed.encode("utf-8"))
56
+ return hashlib.sha256(password.encode()).hexdigest() == hashed
57
+
58
+
59
+ # ---------------------------------------------------------------------------
60
+ # Data classes
61
+ # ---------------------------------------------------------------------------
62
+
63
+ @dataclass
64
+ class UserIdentity:
65
+ """A registered user."""
66
+ user_id: str
67
+ email: str
68
+ display_name: str
69
+ status: str = "active"
70
+ created_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
71
+ last_login_at: Optional[str] = None
72
+ config: Dict[str, Any] = field(default_factory=dict)
73
+ is_super_admin: bool = False
74
+
75
+ def to_dict(self) -> Dict[str, Any]:
76
+ return asdict(self)
77
+
78
+
79
+ @dataclass
80
+ class UserTenantRole:
81
+ """A user's membership in a tenant."""
82
+ user_id: str
83
+ tenant_id: str
84
+ role: str # owner, admin, contributor, reader
85
+ joined_at: str
86
+
87
+
88
+ # ---------------------------------------------------------------------------
89
+ # Registry
90
+ # ---------------------------------------------------------------------------
91
+
92
+ _EMAIL_RE = re.compile(r"^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$")
93
+
94
+ # ---------------------------------------------------------------------------
95
+ # Password strength validation
96
+ # ---------------------------------------------------------------------------
97
+
98
+ _COMMON_PASSWORDS = {
99
+ "password", "123456", "12345678", "qwerty", "abc123", "letmein",
100
+ "admin", "welcome", "monkey", "dragon", "master", "login",
101
+ "password1", "123456789", "1234567890",
102
+ }
103
+
104
+
105
+ def validate_password(password: str) -> Optional[str]:
106
+ """Return error message if password is weak, None if acceptable.
107
+
108
+ Rules (production):
109
+ - Minimum 8 characters
110
+ - At least 1 uppercase, 1 lowercase, 1 digit
111
+ - Not in common password list
112
+
113
+ For testing/dev (AICONTEXTDB_ENV=dev), relaxes to 6 chars min.
114
+ """
115
+ import os
116
+ is_dev = os.environ.get("CONTEXTSYNAPSE_ENV") or os.environ.get("AICONTEXTDB_ENV", "").lower() in ("dev", "development", "test")
117
+ min_len = 6 if is_dev else 8
118
+
119
+ if len(password) < min_len:
120
+ return f"Password must be at least {min_len} characters"
121
+ if password.lower() in _COMMON_PASSWORDS:
122
+ return "Password is too common — choose something more unique"
123
+ if not is_dev:
124
+ if not re.search(r"[A-Z]", password):
125
+ return "Password must contain at least one uppercase letter"
126
+ if not re.search(r"[a-z]", password):
127
+ return "Password must contain at least one lowercase letter"
128
+ if not re.search(r"[0-9]", password):
129
+ return "Password must contain at least one digit"
130
+ return None
131
+
132
+
133
+ class UserRegistry:
134
+ """Persistent user registry backed by SQLite or PostgreSQL."""
135
+
136
+ @staticmethod
137
+ def _schema() -> str:
138
+ return f"""
139
+ CREATE TABLE IF NOT EXISTS users (
140
+ user_id TEXT PRIMARY KEY,
141
+ email TEXT NOT NULL UNIQUE,
142
+ password_hash TEXT NOT NULL,
143
+ display_name TEXT NOT NULL,
144
+ status TEXT DEFAULT 'active',
145
+ created_at TEXT NOT NULL,
146
+ last_login_at TEXT,
147
+ config TEXT DEFAULT '{{}}',
148
+ is_super_admin INTEGER DEFAULT 0
149
+ );
150
+
151
+ CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
152
+ CREATE INDEX IF NOT EXISTS idx_users_status ON users(status);
153
+
154
+ CREATE TABLE IF NOT EXISTS user_tenants (
155
+ user_id TEXT NOT NULL,
156
+ tenant_id TEXT NOT NULL,
157
+ role TEXT DEFAULT 'owner',
158
+ joined_at TEXT NOT NULL,
159
+ PRIMARY KEY (user_id, tenant_id)
160
+ );
161
+
162
+ CREATE INDEX IF NOT EXISTS idx_ut_user ON user_tenants(user_id);
163
+ CREATE INDEX IF NOT EXISTS idx_ut_tenant ON user_tenants(tenant_id);
164
+
165
+ CREATE TABLE IF NOT EXISTS team_invitations (
166
+ invite_id TEXT PRIMARY KEY,
167
+ tenant_id TEXT NOT NULL,
168
+ email TEXT NOT NULL,
169
+ role TEXT DEFAULT 'member',
170
+ invited_by TEXT NOT NULL,
171
+ status TEXT DEFAULT 'pending',
172
+ created_at TEXT NOT NULL,
173
+ UNIQUE(tenant_id, email)
174
+ );
175
+
176
+ CREATE INDEX IF NOT EXISTS idx_inv_tenant ON team_invitations(tenant_id);
177
+ CREATE INDEX IF NOT EXISTS idx_inv_email ON team_invitations(email);
178
+
179
+ CREATE TABLE IF NOT EXISTS login_attempts (
180
+ id {serial_pk()},
181
+ email TEXT NOT NULL,
182
+ ip_address TEXT,
183
+ success INTEGER DEFAULT 0,
184
+ created_at TEXT NOT NULL
185
+ );
186
+
187
+ CREATE INDEX IF NOT EXISTS idx_la_email ON login_attempts(email);
188
+ CREATE INDEX IF NOT EXISTS idx_la_created ON login_attempts(created_at);
189
+
190
+ CREATE TABLE IF NOT EXISTS email_verifications (
191
+ token TEXT PRIMARY KEY,
192
+ user_id TEXT NOT NULL,
193
+ created_at TEXT NOT NULL,
194
+ expires_at TEXT NOT NULL,
195
+ verified_at TEXT
196
+ );
197
+
198
+ CREATE INDEX IF NOT EXISTS idx_ev_user ON email_verifications(user_id);
199
+ """
200
+
201
+ def __init__(self, db_path: str = "contextcore_data/users.db"):
202
+ self._db_path = Path(db_path)
203
+ if not IS_POSTGRES:
204
+ self._db_path.parent.mkdir(parents=True, exist_ok=True)
205
+ self._conn = connect(str(self._db_path))
206
+ run_ddl(self._conn, self._schema())
207
+ self._migrate()
208
+
209
+ def _exec(self, sql: str, params: tuple = ()):
210
+ """Execute SQL and return cursor (works for both SQLite and Postgres)."""
211
+ cur = dict_cursor(self._conn)
212
+ cur.execute(sql, params)
213
+ return cur
214
+
215
+ def _commit(self):
216
+ self._conn.commit()
217
+
218
+ def _migrate(self):
219
+ """Run lightweight schema migrations for existing DBs."""
220
+ if not column_exists(self._conn, "users", "is_super_admin"):
221
+ self._exec(f"ALTER TABLE users ADD COLUMN is_super_admin INTEGER DEFAULT 0")
222
+ self._commit()
223
+ # Normalize legacy role names: viewer→reader, member→contributor
224
+ self._exec("UPDATE user_tenants SET role = 'reader' WHERE role = 'viewer'")
225
+ self._exec("UPDATE user_tenants SET role = 'contributor' WHERE role = 'member'")
226
+ self._commit()
227
+
228
+ def ensure_admin(
229
+ self,
230
+ email: str = "admin@contextsynapse.local",
231
+ password: str = "Admin123!",
232
+ display_name: str = "Admin",
233
+ ) -> Optional[UserIdentity]:
234
+ """Ensure a default admin user exists. Creates one if not found.
235
+
236
+ Called on first startup so there's always a way to log in.
237
+ Returns the admin user, or None if already exists.
238
+ """
239
+ import os
240
+ email = os.environ.get("CONTEXTCORE_ADMIN_EMAIL", email)
241
+ password = os.environ.get("CONTEXTCORE_ADMIN_PASSWORD", password)
242
+ display_name = os.environ.get("CONTEXTCORE_ADMIN_NAME", display_name)
243
+
244
+ cur = self._exec(f"SELECT user_id FROM users WHERE email = {PH}", (email,))
245
+ row = cur.fetchone()
246
+ if row:
247
+ return None
248
+
249
+ user_id = secrets.token_hex(12)
250
+ password_hash = _hash_password(password)
251
+ now = datetime.now(timezone.utc).isoformat()
252
+
253
+ try:
254
+ self._exec(
255
+ f"INSERT INTO users (user_id, email, password_hash, display_name, status, created_at, is_super_admin) "
256
+ f"VALUES ({PH}, {PH}, {PH}, {PH}, 'active', {PH}, 1)",
257
+ (user_id, email, password_hash, display_name, now),
258
+ )
259
+ self._commit()
260
+ logger.info("Default admin created: %s (change password after first login)", email)
261
+ return UserIdentity(
262
+ user_id=user_id, email=email, display_name=display_name,
263
+ created_at=now, is_super_admin=True,
264
+ )
265
+ except Exception as e:
266
+ logger.debug("Admin user creation skipped: %s", e)
267
+ return None
268
+
269
+ # ------------------------------------------------------------------
270
+ # User CRUD
271
+ # ------------------------------------------------------------------
272
+
273
+ def create(
274
+ self,
275
+ email: str,
276
+ password: str,
277
+ display_name: str,
278
+ ) -> UserIdentity:
279
+ """Create a new user. Raises ValueError on invalid input or duplicate email."""
280
+ email = email.strip().lower()
281
+ if not _EMAIL_RE.match(email):
282
+ raise ValueError("Invalid email address")
283
+ pwd_err = validate_password(password)
284
+ if pwd_err:
285
+ raise ValueError(pwd_err)
286
+ display_name = display_name.strip()
287
+ if not display_name:
288
+ raise ValueError("Display name is required")
289
+
290
+ user_id = secrets.token_hex(12)
291
+ password_hash = _hash_password(password)
292
+ now = datetime.now(timezone.utc).isoformat()
293
+
294
+ try:
295
+ self._exec(
296
+ f"INSERT INTO users (user_id, email, password_hash, display_name, status, created_at) "
297
+ f"VALUES ({PH}, {PH}, {PH}, {PH}, 'active', {PH})",
298
+ (user_id, email, password_hash, display_name, now),
299
+ )
300
+ self._commit()
301
+ except integrity_error():
302
+ raise ValueError("A user with this email already exists")
303
+
304
+ return UserIdentity(
305
+ user_id=user_id,
306
+ email=email,
307
+ display_name=display_name,
308
+ created_at=now,
309
+ )
310
+
311
+ # Max failed login attempts before lockout (within window)
312
+ MAX_FAILED_ATTEMPTS = 5
313
+ LOCKOUT_WINDOW_MINUTES = 15
314
+
315
+ def authenticate(self, email: str, password: str,
316
+ ip_address: str = None) -> Optional[UserIdentity]:
317
+ """Validate email + password with brute-force protection.
318
+
319
+ Returns user if valid, None if bad credentials.
320
+ Raises ValueError if account is locked out.
321
+ """
322
+ email = email.strip().lower()
323
+ now = datetime.now(timezone.utc).isoformat()
324
+
325
+ # Check for lockout (too many failed attempts)
326
+ if self._is_locked_out(email):
327
+ raise ValueError(
328
+ f"Account temporarily locked — too many failed attempts. "
329
+ f"Try again in {self.LOCKOUT_WINDOW_MINUTES} minutes."
330
+ )
331
+
332
+ row = row_to_dict(self._exec(
333
+ f"SELECT * FROM users WHERE email = {PH} AND status = 'active'",
334
+ (email,),
335
+ ).fetchone())
336
+
337
+ if not row or not _verify_password(password, row["password_hash"]):
338
+ # Record failed attempt
339
+ self._record_login_attempt(email, ip_address, success=False)
340
+ return None
341
+
342
+ # Success — record and clear lockout
343
+ self._record_login_attempt(email, ip_address, success=True)
344
+
345
+ # Update last_login_at
346
+ self._exec(
347
+ f"UPDATE users SET last_login_at = {PH} WHERE user_id = {PH}",
348
+ (now, row["user_id"]),
349
+ )
350
+ self._commit()
351
+
352
+ user = self._row_to_user(row)
353
+ user.last_login_at = now
354
+ return user
355
+
356
+ def _is_locked_out(self, email: str) -> bool:
357
+ """Check if too many failed login attempts in the lockout window."""
358
+ from datetime import timedelta
359
+ cutoff = (datetime.now(timezone.utc) - timedelta(minutes=self.LOCKOUT_WINDOW_MINUTES)).isoformat()
360
+ row = row_to_dict(self._exec(
361
+ f"SELECT COUNT(*) as cnt FROM login_attempts "
362
+ f"WHERE email = {PH} AND success = 0 AND created_at > {PH}",
363
+ (email, cutoff),
364
+ ).fetchone())
365
+ return (row["cnt"] >= self.MAX_FAILED_ATTEMPTS) if row else False
366
+
367
+ def _record_login_attempt(self, email: str, ip_address: str = None,
368
+ success: bool = False):
369
+ """Record a login attempt for rate limiting."""
370
+ now = datetime.now(timezone.utc).isoformat()
371
+ try:
372
+ self._exec(
373
+ f"INSERT INTO login_attempts (email, ip_address, success, created_at) "
374
+ f"VALUES ({PH}, {PH}, {PH}, {PH})",
375
+ (email, ip_address, 1 if success else 0, now),
376
+ )
377
+ # Clean up old attempts (older than 24h)
378
+ from datetime import timedelta
379
+ cutoff = (datetime.now(timezone.utc) - timedelta(hours=24)).isoformat()
380
+ self._exec(f"DELETE FROM login_attempts WHERE created_at < {PH}", (cutoff,))
381
+ self._commit()
382
+ except Exception:
383
+ pass
384
+
385
+ # ------------------------------------------------------------------
386
+ # Email verification
387
+ # ------------------------------------------------------------------
388
+
389
+ def create_verification_token(self, user_id: str, expires_hours: int = 24) -> str:
390
+ """Create an email verification token. Returns the raw token."""
391
+ from datetime import timedelta
392
+ token = secrets.token_urlsafe(32)
393
+ now = datetime.now(timezone.utc)
394
+ expires = now + timedelta(hours=expires_hours)
395
+ self._exec(
396
+ f"INSERT INTO email_verifications (token, user_id, created_at, expires_at) "
397
+ f"VALUES ({PH}, {PH}, {PH}, {PH})",
398
+ (token, user_id, now.isoformat(), expires.isoformat()),
399
+ )
400
+ self._commit()
401
+ return token
402
+
403
+ def verify_email(self, token: str) -> Optional[str]:
404
+ """Verify an email token. Returns user_id if valid, None otherwise."""
405
+ now = datetime.now(timezone.utc).isoformat()
406
+ row = row_to_dict(self._exec(
407
+ f"SELECT * FROM email_verifications "
408
+ f"WHERE token = {PH} AND verified_at IS NULL AND expires_at > {PH}",
409
+ (token, now),
410
+ ).fetchone())
411
+ if not row:
412
+ return None
413
+ user_id = row["user_id"]
414
+ self._exec(
415
+ f"UPDATE email_verifications SET verified_at = {PH} WHERE token = {PH}",
416
+ (now, token),
417
+ )
418
+ # Mark user as email-verified in config
419
+ user = self.get(user_id)
420
+ if user:
421
+ config = dict(user.config)
422
+ config["email_verified"] = True
423
+ config["email_verified_at"] = now
424
+ self._exec(
425
+ f"UPDATE users SET config = {PH} WHERE user_id = {PH}",
426
+ (json.dumps(config), user_id),
427
+ )
428
+ self._commit()
429
+ return user_id
430
+
431
+ def is_email_verified(self, user_id: str) -> bool:
432
+ """Check if a user's email is verified."""
433
+ user = self.get(user_id)
434
+ if not user:
435
+ return False
436
+ return user.config.get("email_verified", False)
437
+
438
+ def get(self, user_id: str) -> Optional[UserIdentity]:
439
+ row = row_to_dict(self._exec(
440
+ f"SELECT * FROM users WHERE user_id = {PH} AND status != 'deleted'",
441
+ (user_id,),
442
+ ).fetchone())
443
+ return self._row_to_user(row) if row else None
444
+
445
+ def get_by_email(self, email: str) -> Optional[UserIdentity]:
446
+ email = email.strip().lower()
447
+ row = row_to_dict(self._exec(
448
+ f"SELECT * FROM users WHERE email = {PH} AND status != 'deleted'",
449
+ (email,),
450
+ ).fetchone())
451
+ return self._row_to_user(row) if row else None
452
+
453
+ def update(self, user_id: str, display_name: Optional[str] = None,
454
+ password: Optional[str] = None) -> Optional[UserIdentity]:
455
+ """Update user profile fields."""
456
+ user = self.get(user_id)
457
+ if not user:
458
+ return None
459
+
460
+ if display_name:
461
+ self._exec(
462
+ f"UPDATE users SET display_name = {PH} WHERE user_id = {PH}",
463
+ (display_name.strip(), user_id),
464
+ )
465
+ if password:
466
+ if len(password) < 6:
467
+ raise ValueError("Password must be at least 6 characters")
468
+ self._exec(
469
+ f"UPDATE users SET password_hash = {PH} WHERE user_id = {PH}",
470
+ (_hash_password(password), user_id),
471
+ )
472
+ self._commit()
473
+ return self.get(user_id)
474
+
475
+ # ------------------------------------------------------------------
476
+ # User ↔ Tenant membership
477
+ # ------------------------------------------------------------------
478
+
479
+ def link_tenant(self, user_id: str, tenant_id: str, role: str = "owner") -> None:
480
+ """Associate a user with a tenant."""
481
+ now = datetime.now(timezone.utc).isoformat()
482
+ if IS_POSTGRES:
483
+ self._exec(
484
+ f"INSERT INTO user_tenants (user_id, tenant_id, role, joined_at) "
485
+ f"VALUES ({PH}, {PH}, {PH}, {PH}) "
486
+ f"ON CONFLICT (user_id, tenant_id) DO UPDATE SET role = EXCLUDED.role, joined_at = EXCLUDED.joined_at",
487
+ (user_id, tenant_id, role, now),
488
+ )
489
+ else:
490
+ self._exec(
491
+ f"INSERT OR REPLACE INTO user_tenants (user_id, tenant_id, role, joined_at) "
492
+ f"VALUES ({PH}, {PH}, {PH}, {PH})",
493
+ (user_id, tenant_id, role, now),
494
+ )
495
+ self._commit()
496
+
497
+ def get_user_tenants(self, user_id: str) -> List[UserTenantRole]:
498
+ """Get all tenants a user belongs to."""
499
+ rows = self._exec(
500
+ f"SELECT * FROM user_tenants WHERE user_id = {PH}",
501
+ (user_id,),
502
+ ).fetchall()
503
+ return [
504
+ UserTenantRole(
505
+ user_id=r["user_id"],
506
+ tenant_id=r["tenant_id"],
507
+ role=r["role"],
508
+ joined_at=r["joined_at"],
509
+ )
510
+ for r in [row_to_dict(r) for r in rows]
511
+ ]
512
+
513
+ def get_tenant_members(self, tenant_id: str) -> List[UserTenantRole]:
514
+ """Get all users in a tenant."""
515
+ rows = self._exec(
516
+ f"SELECT * FROM user_tenants WHERE tenant_id = {PH}",
517
+ (tenant_id,),
518
+ ).fetchall()
519
+ return [
520
+ UserTenantRole(
521
+ user_id=r["user_id"],
522
+ tenant_id=r["tenant_id"],
523
+ role=r["role"],
524
+ joined_at=r["joined_at"],
525
+ )
526
+ for r in [row_to_dict(r) for r in rows]
527
+ ]
528
+
529
+ def get_primary_tenant_id(self, user_id: str) -> Optional[str]:
530
+ """Get the first (owner) tenant for a user."""
531
+ row = row_to_dict(self._exec(
532
+ f"SELECT tenant_id FROM user_tenants WHERE user_id = {PH} AND role = 'owner' LIMIT 1",
533
+ (user_id,),
534
+ ).fetchone())
535
+ if row:
536
+ return row["tenant_id"]
537
+ # Fallback to any tenant
538
+ row = row_to_dict(self._exec(
539
+ f"SELECT tenant_id FROM user_tenants WHERE user_id = {PH} LIMIT 1",
540
+ (user_id,),
541
+ ).fetchone())
542
+ return row["tenant_id"] if row else None
543
+
544
+ # ------------------------------------------------------------------
545
+ # Team invitations
546
+ # ------------------------------------------------------------------
547
+
548
+ def create_invitation(self, tenant_id: str, email: str, role: str, invited_by: str) -> Dict[str, Any]:
549
+ """Create a team invitation. Returns the invitation dict."""
550
+ email = email.strip().lower()
551
+ invite_id = secrets.token_hex(12)
552
+ now = datetime.now(timezone.utc).isoformat()
553
+
554
+ try:
555
+ self._exec(
556
+ f"INSERT INTO team_invitations (invite_id, tenant_id, email, role, invited_by, status, created_at) "
557
+ f"VALUES ({PH}, {PH}, {PH}, {PH}, {PH}, 'pending', {PH})",
558
+ (invite_id, tenant_id, email, role, invited_by, now),
559
+ )
560
+ self._commit()
561
+ except integrity_error():
562
+ raise ValueError("An invitation for this email already exists")
563
+
564
+ return {"invite_id": invite_id, "tenant_id": tenant_id, "email": email, "role": role, "status": "pending", "created_at": now}
565
+
566
+ def list_invitations(self, tenant_id: str) -> List[Dict[str, Any]]:
567
+ """List all pending invitations for a tenant."""
568
+ rows = self._exec(
569
+ f"SELECT * FROM team_invitations WHERE tenant_id = {PH} AND status = 'pending' ORDER BY created_at DESC",
570
+ (tenant_id,),
571
+ ).fetchall()
572
+ return [row_to_dict(r) for r in rows]
573
+
574
+ def accept_invitation(self, invite_id: str, user_id: str) -> bool:
575
+ """Accept an invitation — link user to tenant."""
576
+ row = row_to_dict(self._exec(
577
+ f"SELECT * FROM team_invitations WHERE invite_id = {PH} AND status = 'pending'",
578
+ (invite_id,),
579
+ ).fetchone())
580
+ if not row:
581
+ return False
582
+
583
+ self.link_tenant(user_id, row["tenant_id"], role=row["role"])
584
+ self._exec(
585
+ f"UPDATE team_invitations SET status = 'accepted' WHERE invite_id = {PH}",
586
+ (invite_id,),
587
+ )
588
+ self._commit()
589
+ return True
590
+
591
+ def revoke_invitation(self, invite_id: str) -> bool:
592
+ """Revoke a pending invitation."""
593
+ cur = self._exec(
594
+ f"UPDATE team_invitations SET status = 'revoked' WHERE invite_id = {PH} AND status = 'pending'",
595
+ (invite_id,),
596
+ )
597
+ self._commit()
598
+ return cur.rowcount > 0
599
+
600
+ def remove_member(self, tenant_id: str, user_id: str) -> bool:
601
+ """Remove a user from a tenant."""
602
+ cur = self._exec(
603
+ f"DELETE FROM user_tenants WHERE tenant_id = {PH} AND user_id = {PH} AND role != 'owner'",
604
+ (tenant_id, user_id),
605
+ )
606
+ self._commit()
607
+ return cur.rowcount > 0
608
+
609
+ def update_member_role(self, tenant_id: str, user_id: str, role: str) -> bool:
610
+ """Update a member's role (cannot change owner)."""
611
+ cur = self._exec(
612
+ f"UPDATE user_tenants SET role = {PH} WHERE tenant_id = {PH} AND user_id = {PH} AND role != 'owner'",
613
+ (role, tenant_id, user_id),
614
+ )
615
+ self._commit()
616
+ return cur.rowcount > 0
617
+
618
+ # ------------------------------------------------------------------
619
+ # Helpers
620
+ # ------------------------------------------------------------------
621
+
622
+ def _row_to_user(self, row) -> UserIdentity:
623
+ d = row_to_dict(row) if not isinstance(row, dict) else row
624
+ return UserIdentity(
625
+ user_id=d["user_id"],
626
+ email=d["email"],
627
+ display_name=d["display_name"],
628
+ status=d["status"],
629
+ created_at=d["created_at"],
630
+ last_login_at=d.get("last_login_at"),
631
+ config=json.loads(d["config"]) if d.get("config") else {},
632
+ is_super_admin=bool(d.get("is_super_admin", 0)),
633
+ )
634
+
635
+ def set_super_admin(self, user_id: str, is_super_admin: bool = True) -> bool:
636
+ """Promote or demote a user to/from super_admin (platform-wide)."""
637
+ cur = self._exec(
638
+ f"UPDATE users SET is_super_admin = {PH} WHERE user_id = {PH}",
639
+ (1 if is_super_admin else 0, user_id),
640
+ )
641
+ self._commit()
642
+ return cur.rowcount > 0
643
+
644
+ def close(self):
645
+ if self._conn:
646
+ self._conn.close()