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,978 @@
1
+ """
2
+ Agent Worker Router
3
+ ====================
4
+ REST API for remote agents to poll tasks, claim work, report results,
5
+ and interact with the shared graph — all authenticated via agent_id:secret.
6
+
7
+ Mount on the main FastAPI app::
8
+
9
+ from contextsynapse.api.agent_worker_router import create_agent_worker_router
10
+ app.include_router(create_agent_worker_router(graph_registry))
11
+
12
+ Endpoints:
13
+ GET /agent/sessions — List agent's sessions
14
+ GET /agent/sessions/{sid}/tasks/mine — Poll assigned tasks
15
+ GET /agent/sessions/{sid}/tasks/available — Poll unassigned tasks
16
+ POST /agent/sessions/{sid}/tasks/{tid}/claim — Claim a task
17
+ POST /agent/sessions/{sid}/tasks/{tid}/complete — Report completion
18
+ POST /agent/sessions/{sid}/tasks/{tid}/handoff — Hand off to another agent
19
+ POST /agent/sessions/{sid}/heartbeat — Status heartbeat
20
+ GET /agent/sessions/{sid}/workspace/config — Get workspace config
21
+ POST /agent/sessions/{sid}/workspace/artifacts — Upload files (no-git agents)
22
+ POST /agent/sessions/{sid}/workspace/push-event — Notify of git push
23
+ POST /agent/sessions/{sid}/graph/nodes — Add knowledge node
24
+ POST /agent/sessions/{sid}/graph/query — Execute AIQL query
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import json
30
+ import logging
31
+ from datetime import datetime, timezone
32
+ from typing import Any, Dict, List, Optional
33
+
34
+ from fastapi import APIRouter, Depends, HTTPException, Query, Request
35
+ from pydantic import BaseModel, Field
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ # ── Platform → Tier Mapping ──────────────────────────────────────────
41
+
42
+ _PLATFORM_TIER: dict[str, str] = {
43
+ "embedded": "instant",
44
+ "mobile": "fast",
45
+ "browser": "fast",
46
+ "desktop": "standard",
47
+ "app": "standard",
48
+ }
49
+
50
+
51
+ def _tier_for_platform(platform: str | None, explicit_tier: str | None) -> str:
52
+ """Resolve projection tier: explicit param wins; otherwise use platform default."""
53
+ if explicit_tier is not None:
54
+ return explicit_tier
55
+ return _PLATFORM_TIER.get(platform or "", "standard")
56
+
57
+
58
+ _TIER_BUDGETS: dict[str, dict] = {
59
+ "instant": {"token_budget": 500, "time_budget_ms": 50},
60
+ "fast": {"token_budget": 1500, "time_budget_ms": 200},
61
+ "standard":{"token_budget": 3000, "time_budget_ms": 1000},
62
+ "deep": {"token_budget": 8000, "time_budget_ms": 5000},
63
+ }
64
+
65
+
66
+ def _build_platform_hints(platform: str | None, resolved_tier: str, tier_was_explicit: bool) -> dict:
67
+ """Build the platform_hints block for orient_agent responses."""
68
+ budgets = _TIER_BUDGETS.get(resolved_tier, _TIER_BUDGETS["standard"])
69
+ if tier_was_explicit:
70
+ note = "Tier explicitly set by caller."
71
+ else:
72
+ note = f"Tier auto-selected for {platform or 'unknown'} platform. Pass ?tier=deep to override."
73
+ return {
74
+ "platform": platform or "unknown",
75
+ "tier": resolved_tier,
76
+ **budgets,
77
+ "note": note,
78
+ }
79
+
80
+
81
+ # ── Request/Response Models ─────────────────────────────────────────
82
+
83
+ class TaskCompleteRequest(BaseModel):
84
+ summary: str
85
+ files_changed: List[str] = Field(default_factory=list)
86
+
87
+ class TaskHandoffRequest(BaseModel):
88
+ to_agent: str
89
+ notes: str = ""
90
+
91
+ class HeartbeatRequest(BaseModel):
92
+ status: str = "idle" # idle | working | error
93
+ current_task_id: Optional[str] = None
94
+ progress: float = 0.0
95
+ message: str = ""
96
+
97
+ class ArtifactUploadRequest(BaseModel):
98
+ task_id: Optional[str] = None
99
+ files: List[Dict[str, str]] # [{"path": "src/app.py", "content": "..."}]
100
+ commit_message: str = "Agent artifact upload"
101
+
102
+ class TaskCreateRequest(BaseModel):
103
+ title: str
104
+ description: str = ""
105
+ priority: str = "medium"
106
+ assigned_to: str = ""
107
+ tags: str = ""
108
+ depends_on: str = ""
109
+ parent_task: str = ""
110
+
111
+ class GraphEdgeRequest(BaseModel):
112
+ source: str
113
+ target: str
114
+ label: str
115
+ properties: Dict[str, Any] = Field(default_factory=dict)
116
+
117
+ class GraphSearchRequest(BaseModel):
118
+ label: str = ""
119
+ query: str = ""
120
+ where: Dict[str, Any] = Field(default_factory=dict)
121
+ limit: int = 10
122
+
123
+ class PushEventRequest(BaseModel):
124
+ branch: str
125
+ commit_sha: str = ""
126
+ files_changed: List[str] = Field(default_factory=list)
127
+ task_id: Optional[str] = None
128
+
129
+ class GraphNodeRequest(BaseModel):
130
+ label: str
131
+ properties: Dict[str, Any] = Field(default_factory=dict)
132
+
133
+ class GraphQueryRequest(BaseModel):
134
+ aiql: str
135
+
136
+
137
+ # ── Module-level stubs for internal helpers (patchable in tests) ─────
138
+ # These are overridden inside create_agent_worker_router closures but
139
+ # exposed here so unit tests can patch them via their module path.
140
+ def _get_session_or_404(session_id: str): # pragma: no cover
141
+ raise NotImplementedError("call create_agent_worker_router first")
142
+
143
+ def _get_connection(session): # pragma: no cover
144
+ raise NotImplementedError("call create_agent_worker_router first")
145
+
146
+ def _get_runtime_connection(session): # pragma: no cover
147
+ raise NotImplementedError("call create_agent_worker_router first")
148
+
149
+ def _get_project_graph(session): # pragma: no cover
150
+ raise NotImplementedError("call create_agent_worker_router first")
151
+
152
+
153
+ # ── Router Factory ──────────────────────────────────────────────────
154
+
155
+ def create_agent_worker_router(graph_registry=None) -> APIRouter:
156
+ """Build the /agent router for remote agent workers."""
157
+
158
+ router = APIRouter(prefix="/agent", tags=["Agent Worker"])
159
+
160
+ from ..context.store_factory import create_agent_registry
161
+ from ..context.session import ContextSessionManager
162
+ from .auth import AgentAuth
163
+ from .events import event_bus
164
+
165
+ agent_registry = create_agent_registry()
166
+ session_manager = ContextSessionManager(graph_registry=graph_registry)
167
+ require_agent = AgentAuth(agent_registry)
168
+
169
+ def _get_session_or_404(session_id: str):
170
+ session = session_manager.get_session(session_id)
171
+ if not session:
172
+ raise HTTPException(404, f"Session '{session_id}' not found")
173
+ return session
174
+
175
+ def _get_project_graph(session):
176
+ """ProjectGraph targets the runtime namespace (tasks, findings, actions)."""
177
+ from ..project.graph import ProjectGraph
178
+ from ..adapters._base import AIContextDBConnection
179
+ rt_ns = getattr(session, 'runtime_namespace', '') or f"{session.graph_namespace}_rt"
180
+ conn = AIContextDBConnection(namespace=rt_ns, graph_registry=graph_registry)
181
+ return ProjectGraph(rt_ns, connection=conn, agent_registry=agent_registry)
182
+
183
+ def _get_connection(session):
184
+ """Connection targets the atomic namespace (source knowledge)."""
185
+ from ..adapters._base import AIContextDBConnection
186
+ return AIContextDBConnection(namespace=session.graph_namespace, graph_registry=graph_registry)
187
+
188
+ def _get_runtime_connection(session):
189
+ """Connection to the runtime graph (agent work)."""
190
+ from ..adapters._base import AIContextDBConnection
191
+ rt_ns = getattr(session, 'runtime_namespace', '') or f"{session.graph_namespace}_rt"
192
+ return AIContextDBConnection(namespace=rt_ns, graph_registry=graph_registry)
193
+
194
+ # ── Session Discovery ───────────────────────────────────────────
195
+
196
+ @router.get("/sessions")
197
+ async def list_my_sessions(agent=Depends(require_agent)):
198
+ """List sessions this agent has access to, with pending task counts."""
199
+ all_sessions = session_manager.list_sessions()
200
+ results = []
201
+ for s in all_sessions:
202
+ if s.status != "active":
203
+ continue
204
+ access_list = session_manager.get_access_list(s.session_id)
205
+ agent_ids = [a["agent_id"] for a in access_list if isinstance(a, dict)]
206
+ if agent.agent_id in agent_ids:
207
+ # Count pending tasks
208
+ try:
209
+ pg = _get_project_graph(s)
210
+ open_tasks = pg.get_open_tasks(agent_id=agent.agent_id, agent_name=agent.name)
211
+ all_open = pg.get_open_tasks()
212
+ unassigned = [t for t in all_open if t["assigned_to"] in ("unassigned", "")]
213
+ except Exception:
214
+ open_tasks, unassigned = [], []
215
+
216
+ results.append({
217
+ "session_id": s.session_id,
218
+ "name": s.name,
219
+ "status": s.status,
220
+ "graph_namespace": s.graph_namespace,
221
+ "my_tasks": len(open_tasks),
222
+ "unassigned_tasks": len(unassigned),
223
+ "goal": (s.config or {}).get("goal", ""),
224
+ })
225
+ return {"sessions": results}
226
+
227
+ # ── Task Polling ────────────────────────────────────────────────
228
+
229
+ @router.get("/sessions/{session_id}/tasks/mine")
230
+ async def my_tasks(
231
+ session_id: str,
232
+ status: str = Query("all", description="open|in_progress|all"),
233
+ agent=Depends(require_agent),
234
+ ):
235
+ """Get tasks assigned to this agent."""
236
+ session = _get_session_or_404(session_id)
237
+ pg = _get_project_graph(session)
238
+ tasks = pg.get_open_tasks(agent_id=agent.agent_id, agent_name=agent.name)
239
+ if status != "all":
240
+ tasks = [t for t in tasks if t["status"] == status]
241
+ return {"tasks": tasks, "count": len(tasks)}
242
+
243
+ @router.get("/sessions/{session_id}/tasks/available")
244
+ async def available_tasks(
245
+ session_id: str,
246
+ priority: str = Query("", description="Filter by priority"),
247
+ agent=Depends(require_agent),
248
+ ):
249
+ """Get open unassigned tasks available to claim."""
250
+ session = _get_session_or_404(session_id)
251
+ pg = _get_project_graph(session)
252
+ all_open = pg.get_open_tasks()
253
+ available = [t for t in all_open if t["assigned_to"] in ("unassigned", "", None)]
254
+ if priority:
255
+ available = [t for t in available if t["priority"] == priority]
256
+ return {"tasks": available, "count": len(available)}
257
+
258
+ @router.get("/sessions/{session_id}/tasks/stream")
259
+ async def stream_tasks(session_id: str, agent=Depends(require_agent)):
260
+ """SSE endpoint — push task notifications to agents in real-time.
261
+
262
+ Instead of polling /tasks/available every N seconds, agents connect here
263
+ and receive instant notifications when tasks are created or unblocked.
264
+ Falls back: if no events within 30s, sends a heartbeat with pending count.
265
+ """
266
+ import asyncio
267
+ from starlette.responses import StreamingResponse
268
+
269
+ session = _get_session_or_404(session_id)
270
+
271
+ async def event_generator():
272
+ queue = event_bus.subscribe()
273
+ try:
274
+ while True:
275
+ try:
276
+ event = await asyncio.wait_for(queue.get(), timeout=30)
277
+ if event.get("type") in ("task_available", "task_unblocked", "task_created"):
278
+ yield f"data: {json.dumps(event)}\n\n"
279
+ except asyncio.TimeoutError:
280
+ # Heartbeat with pending task count
281
+ pg = _get_project_graph(session)
282
+ open_tasks = pg.get_open_tasks()
283
+ available = [t for t in open_tasks if t["assigned_to"] in ("unassigned", "", None)]
284
+ yield f"data: {json.dumps({'type': 'heartbeat', 'available': len(available)})}\n\n"
285
+ except asyncio.CancelledError:
286
+ pass
287
+ finally:
288
+ event_bus.unsubscribe(queue)
289
+
290
+ return StreamingResponse(event_generator(), media_type="text/event-stream")
291
+
292
+ # ── Task Lifecycle ──────────────────────────────────────────────
293
+
294
+ @router.post("/sessions/{session_id}/tasks/{task_id}/claim")
295
+ async def claim_task(session_id: str, task_id: str, agent=Depends(require_agent)):
296
+ """Claim an open task."""
297
+ session = _get_session_or_404(session_id)
298
+ pg = _get_project_graph(session)
299
+ result = pg.claim_task(task_id, agent.agent_id)
300
+ if result.startswith("Error"):
301
+ raise HTTPException(409, result)
302
+ agent_registry.touch(agent.agent_id)
303
+ return {"status": "claimed", "task_id": task_id, "message": result}
304
+
305
+ @router.post("/sessions/{session_id}/tasks/{task_id}/complete")
306
+ async def complete_task(
307
+ session_id: str, task_id: str, req: TaskCompleteRequest, agent=Depends(require_agent),
308
+ ):
309
+ """Report task completion."""
310
+ session = _get_session_or_404(session_id)
311
+ pg = _get_project_graph(session)
312
+ result = pg.complete_task(
313
+ task_id, agent.agent_id, summary=req.summary,
314
+ files_changed=req.files_changed or None,
315
+ )
316
+ if result.startswith("Error"):
317
+ raise HTTPException(400, result)
318
+ agent_registry.touch(agent.agent_id)
319
+ return {"status": "completed", "task_id": task_id, "message": result}
320
+
321
+ @router.post("/sessions/{session_id}/tasks/{task_id}/handoff")
322
+ async def handoff_task(
323
+ session_id: str, task_id: str, req: TaskHandoffRequest, agent=Depends(require_agent),
324
+ ):
325
+ """Hand off a task to another agent."""
326
+ session = _get_session_or_404(session_id)
327
+ pg = _get_project_graph(session)
328
+ result = pg.handoff_task(task_id, from_agent=agent.agent_id, to_agent=req.to_agent, notes=req.notes)
329
+ if result.startswith("Error"):
330
+ raise HTTPException(400, result)
331
+ return {"status": "handed_off", "task_id": task_id, "message": result}
332
+
333
+ @router.post("/sessions/{session_id}/tasks")
334
+ async def create_task(session_id: str, req: TaskCreateRequest, agent=Depends(require_agent)):
335
+ """Create a new task (or break a task into subtasks)."""
336
+ session = _get_session_or_404(session_id)
337
+ pg = _get_project_graph(session)
338
+ dep_list = [d.strip() for d in req.depends_on.split(",") if d.strip()] if req.depends_on else None
339
+ tag_list = [t.strip() for t in req.tags.split(",") if t.strip()] if req.tags else None
340
+ task_id = pg.add_task(
341
+ title=req.title, description=req.description,
342
+ priority=req.priority, assigned_to=req.assigned_to or None,
343
+ tags=tag_list, depends_on=dep_list,
344
+ parent_task=req.parent_task or None,
345
+ created_by=agent.name,
346
+ )
347
+ return {"task_id": task_id, "title": req.title, "assigned_to": req.assigned_to, "parent_task": req.parent_task}
348
+
349
+ @router.get("/sessions/{session_id}/tasks")
350
+ async def list_all_tasks(
351
+ session_id: str,
352
+ status: str = Query("", description="open|in_progress|completed|all"),
353
+ agent=Depends(require_agent),
354
+ ):
355
+ """List all tasks in the session."""
356
+ session = _get_session_or_404(session_id)
357
+ pg = _get_project_graph(session)
358
+ tasks = pg.get_open_tasks()
359
+ if status and status != "all":
360
+ tasks = [t for t in tasks if t["status"] == status]
361
+ return {"tasks": tasks, "count": len(tasks)}
362
+
363
+ # ── Heartbeat ───────────────────────────────────────────────────
364
+
365
+ @router.post("/sessions/{session_id}/heartbeat")
366
+ async def heartbeat(session_id: str, req: HeartbeatRequest, agent=Depends(require_agent)):
367
+ """Agent status heartbeat — updates last_seen, returns pending task count."""
368
+ _get_session_or_404(session_id)
369
+ agent_registry.touch(agent.agent_id)
370
+
371
+ # Count pending tasks
372
+ try:
373
+ session = session_manager.get_session(session_id)
374
+ pg = _get_project_graph(session)
375
+ my_tasks = pg.get_open_tasks(agent_id=agent.agent_id, agent_name=agent.name)
376
+ pending = len(my_tasks)
377
+ except Exception:
378
+ pending = 0
379
+
380
+ return {
381
+ "ack": True,
382
+ "server_time": datetime.now(timezone.utc).isoformat(),
383
+ "pending_tasks": pending,
384
+ "agent_status": req.status,
385
+ }
386
+
387
+ # ── Workspace ───────────────────────────────────────────────────
388
+
389
+ @router.get("/sessions/{session_id}/workspace/config")
390
+ async def get_workspace_config(session_id: str, agent=Depends(require_agent)):
391
+ """Get workspace configuration for this session."""
392
+ session = _get_session_or_404(session_id)
393
+ config = (session.config or {}).get("workspace", {"type": "local"})
394
+
395
+ # Add computed agent-specific branch name
396
+ branch_pattern = config.get("branch_pattern", "agent/{agent_name}/{task_id}")
397
+ config["agent_branch_prefix"] = f"agent/{agent.name}"
398
+
399
+ # Remove sensitive fields for non-admin agents
400
+ safe_config = dict(config)
401
+ if agent.role != "admin" and "admin" not in (agent.capabilities or []):
402
+ safe_config.pop("github_token", None)
403
+
404
+ return {"workspace": safe_config, "session_name": session.name}
405
+
406
+ @router.post("/sessions/{session_id}/workspace/artifacts")
407
+ async def upload_artifacts(
408
+ session_id: str, req: ArtifactUploadRequest, agent=Depends(require_agent),
409
+ ):
410
+ """Upload files for agents that can't use git directly."""
411
+ session = _get_session_or_404(session_id)
412
+ ws_config = (session.config or {}).get("workspace", {"type": "local", "path": f"generated/{session.name.replace(' ', '-').lower()}"})
413
+
414
+ from ..workspace.base import Workspace
415
+ ws = Workspace.from_config(ws_config)
416
+
417
+ written = []
418
+ for f in req.files:
419
+ result = ws.write_file(f["path"], f["content"])
420
+ written.append(f["path"])
421
+
422
+ # Commit if git workspace
423
+ commit_result = ws.commit(req.commit_message)
424
+
425
+ # Record in graph
426
+ conn = _get_connection(session)
427
+ conn.add_node(label="ArtifactUpload", properties={
428
+ "agent_id": agent.agent_id,
429
+ "agent_name": agent.name,
430
+ "files": ",".join(written),
431
+ "task_id": req.task_id or "",
432
+ "timestamp": datetime.now(timezone.utc).isoformat(),
433
+ })
434
+
435
+ return {"status": "received", "files_written": len(written), "commit": commit_result}
436
+
437
+ @router.post("/sessions/{session_id}/workspace/push-event")
438
+ async def push_event(
439
+ session_id: str, req: PushEventRequest, agent=Depends(require_agent),
440
+ ):
441
+ """Notify server that agent pushed to the git remote."""
442
+ session = _get_session_or_404(session_id)
443
+ conn = _get_runtime_connection(session) # agent action → runtime graph
444
+
445
+ # Record push event in graph
446
+ conn.add_node(label="GitPush", properties={
447
+ "agent_id": agent.agent_id,
448
+ "agent_name": agent.name,
449
+ "branch": req.branch,
450
+ "commit_sha": req.commit_sha,
451
+ "files_changed": ",".join(req.files_changed),
452
+ "task_id": req.task_id or "",
453
+ "timestamp": datetime.now(timezone.utc).isoformat(),
454
+ })
455
+
456
+ agent_registry.touch(agent.agent_id)
457
+ return {"status": "recorded", "branch": req.branch}
458
+
459
+ # ── Graph Access ────────────────────────────────────────────────
460
+
461
+ @router.post("/sessions/{session_id}/graph/nodes")
462
+ async def add_graph_node(
463
+ session_id: str, req: GraphNodeRequest, agent=Depends(require_agent),
464
+ ):
465
+ """Add a knowledge node to the session's graph."""
466
+ session = _get_session_or_404(session_id)
467
+ # All agent writes go to runtime graph — agents never write directly to atomic.
468
+ # Atomic context is populated only by the ingestion pipeline.
469
+ # Nodes keep their label (Fact, Knowledge, etc.) but live in runtime
470
+ # until explicitly promoted to atomic.
471
+ conn = _get_runtime_connection(session)
472
+
473
+ props = dict(req.properties)
474
+ props["_agent_id"] = agent.agent_id
475
+ props["_agent_name"] = agent.name
476
+
477
+ # Use AIQL to ensure node goes to the correct namespace
478
+ prop_parts = []
479
+ for k, v in props.items():
480
+ if isinstance(v, str):
481
+ escaped = v.replace('"', '\\"')
482
+ prop_parts.append(f'{k}: "{escaped}"')
483
+ elif isinstance(v, bool):
484
+ prop_parts.append(f'{k}: {"TRUE" if v else "FALSE"}')
485
+ elif isinstance(v, (int, float)):
486
+ prop_parts.append(f'{k}: {v}')
487
+ else:
488
+ escaped = str(v).replace('"', '\\"')
489
+ prop_parts.append(f'{k}: "{escaped}"')
490
+ prop_str = ", ".join(prop_parts)
491
+ result = conn.query(f'CREATE NODE {req.label} {{{prop_str}}}')
492
+ node_id = result.get("data", {}).get("uuid", "")
493
+ return {"node_id": node_id, "label": req.label}
494
+
495
+ @router.post("/sessions/{session_id}/graph/query")
496
+ async def query_graph(
497
+ session_id: str, req: GraphQueryRequest, agent=Depends(require_agent),
498
+ ):
499
+ """Execute an AIQL query against the session's graph."""
500
+ session = _get_session_or_404(session_id)
501
+ conn = _get_connection(session)
502
+ result = conn.query(req.aiql)
503
+ return result
504
+
505
+ @router.post("/sessions/{session_id}/graph/edges")
506
+ async def add_graph_edge(
507
+ session_id: str, req: GraphEdgeRequest, agent=Depends(require_agent),
508
+ ):
509
+ """Create an edge between two nodes."""
510
+ session = _get_session_or_404(session_id)
511
+ conn = _get_connection(session)
512
+ props = dict(req.properties)
513
+ props["_agent_id"] = agent.agent_id
514
+ edge = conn.add_edge(source=req.source, target=req.target, label=req.label, properties=props)
515
+ return {"edge_id": edge.id, "label": req.label, "source": req.source, "target": req.target}
516
+
517
+ @router.post("/sessions/{session_id}/graph/search")
518
+ async def search_graph_nodes(
519
+ session_id: str, req: GraphSearchRequest, agent=Depends(require_agent),
520
+ ):
521
+ """Search nodes by label, query text, and/or property filters."""
522
+ session = _get_session_or_404(session_id)
523
+ conn = _get_connection(session)
524
+
525
+ # If query text provided, use search_nodes tool (keyword + semantic)
526
+ if req.query and req.query.strip():
527
+ try:
528
+ from ..tools import ToolRegistry
529
+ from .playground_conn import PlaygroundConn, create_tool_context
530
+ _conn = PlaygroundConn(session.graph_namespace, graph_registry)
531
+ _ctx = create_tool_context(_conn, agent.agent_id, agent.name)
532
+ params = {"query": req.query, "limit": str(req.limit)}
533
+ if req.label:
534
+ params["label"] = req.label
535
+ result_text = ToolRegistry.dispatch("search_nodes", _ctx, params)
536
+ return {"results": result_text, "query": req.query, "label": req.label}
537
+ except Exception:
538
+ pass # fallback to AIQL
539
+
540
+ # Fallback: AIQL SELECT
541
+ from ..security.sanitize import sanitize_aiql_identifier, build_safe_where_clause
542
+ where_clause = build_safe_where_clause(req.where)
543
+ safe_label = sanitize_aiql_identifier(req.label) if req.label else ""
544
+ query = f"SELECT * FROM {safe_label}{where_clause}" if safe_label else f"SELECT *{where_clause}"
545
+ result = conn.query(query)
546
+ nodes = result.get("nodes", [])
547
+ # Limit results and truncate content to prevent ResponseTooLargeError
548
+ limit = req.limit if hasattr(req, 'limit') and req.limit else 10
549
+ truncated = []
550
+ for n in nodes[:limit]:
551
+ if isinstance(n, dict):
552
+ props = n.get("properties", n)
553
+ truncated.append({
554
+ "label": n.get("label", n.get("node_type", "?")),
555
+ "name": (props.get("name", "") or "")[:100],
556
+ "content": (props.get("content", props.get("statement", "")) or "")[:200],
557
+ })
558
+ else:
559
+ props = n.properties if hasattr(n, 'properties') else {}
560
+ truncated.append({
561
+ "label": n.label if hasattr(n, 'label') else "?",
562
+ "name": (props.get("name", "") or "")[:100],
563
+ "content": (props.get("content", props.get("statement", "")) or "")[:200],
564
+ })
565
+ return {"nodes": truncated, "count": len(nodes), "showing": len(truncated)}
566
+
567
+ # ── Agent Activity ─────────────────────────────────────────
568
+
569
+ @router.get("/sessions/{session_id}/activity")
570
+ async def agent_session_activity(
571
+ session_id: str, limit: int = Query(50), agent=Depends(require_agent),
572
+ ):
573
+ """Get agent activity for this session — who wrote what, when."""
574
+ session = _get_session_or_404(session_id)
575
+ conn = _get_connection(session)
576
+
577
+ # Find all nodes with _agent_id or _agent_name or created_by
578
+ activity = []
579
+ try:
580
+ for label in ["Finding", "Insight", "Decision", "Task", "ToolCall"]:
581
+ nodes = conn.db.get_all_nodes(label=label) if hasattr(conn, 'db') and conn.db else []
582
+ for n in nodes:
583
+ props = n.properties if hasattr(n, 'properties') else {}
584
+ agent_name = props.get("_agent_name", props.get("created_by", props.get("source", "")))
585
+ if agent_name:
586
+ activity.append({
587
+ "id": n.id if hasattr(n, 'id') else "",
588
+ "label": n.label if hasattr(n, 'label') else label,
589
+ "agent": agent_name,
590
+ "name": (props.get("name", "") or "")[:100],
591
+ "content": (props.get("content", props.get("statement", "")) or "")[:200],
592
+ "created_at": props.get("created_at", ""),
593
+ })
594
+ except Exception:
595
+ pass
596
+
597
+ # Sort by created_at descending
598
+ activity.sort(key=lambda x: x.get("created_at", ""), reverse=True)
599
+ return {"activity": activity[:limit], "total": len(activity)}
600
+
601
+ # ── Context Gaps ──────────────────────────────────────────
602
+
603
+ @router.get("/sessions/{session_id}/gaps")
604
+ async def get_context_gaps(session_id: str, limit: int = Query(20), agent=Depends(require_agent)):
605
+ """Get top context gaps — what agents searched for but couldn't find."""
606
+ session = _get_session_or_404(session_id)
607
+ from ..intelligence.context_gaps import get_top_gaps, get_gap_stats
608
+ gaps = get_top_gaps(namespace=session.graph_namespace, limit=limit)
609
+ stats = get_gap_stats(namespace=session.graph_namespace)
610
+ return {"gaps": gaps, "stats": stats}
611
+
612
+ # ── GPT Action Schema (auto-generated) ──────────────────────
613
+
614
+ @router.get("/openapi.json")
615
+ async def agent_openapi_schema(request: Request):
616
+ """Auto-generated OpenAPI schema for GPT Custom Actions.
617
+
618
+ ChatGPT can import this directly:
619
+ 1. In GPT editor → Actions → Import URL
620
+ 2. Paste: https://your-server/agent/openapi.json
621
+ """
622
+ # Build absolute URL from request (works behind ngrok/proxy)
623
+ forwarded_host = request.headers.get("x-forwarded-host", "")
624
+ forwarded_proto = request.headers.get("x-forwarded-proto", "https")
625
+ if forwarded_host:
626
+ host = f"{forwarded_proto}://{forwarded_host}"
627
+ else:
628
+ host = f"{request.url.scheme}://{request.url.netloc}"
629
+
630
+ return {
631
+ "openapi": "3.1.0",
632
+ "info": {
633
+ "title": "AIContextDB Agent API",
634
+ "description": "Shared knowledge graph for AI agents. Search, write, dispatch goals.",
635
+ "version": "1.1.0",
636
+ },
637
+ "servers": [{"url": f"{host}/agent" if host else "/agent"}],
638
+ "paths": {
639
+ "/sessions/{session_id}/graph/search": {
640
+ "post": {
641
+ "operationId": "searchGraph",
642
+ "summary": "Search the knowledge graph",
643
+ "parameters": [{"name": "session_id", "in": "path", "required": True, "schema": {"type": "string"}}],
644
+ "requestBody": {"required": True, "content": {"application/json": {"schema": {
645
+ "type": "object",
646
+ "properties": {
647
+ "label": {"type": "string", "description": "Node type filter"},
648
+ "query": {"type": "string", "description": "Search keywords"},
649
+ "limit": {"type": "integer", "default": 10},
650
+ },
651
+ }}}},
652
+ "responses": {"200": {"description": "Search results"}},
653
+ }
654
+ },
655
+ "/sessions/{session_id}/graph/nodes": {
656
+ "post": {
657
+ "operationId": "addNode",
658
+ "summary": "Add a node to the graph",
659
+ "parameters": [{"name": "session_id", "in": "path", "required": True, "schema": {"type": "string"}}],
660
+ "requestBody": {"required": True, "content": {"application/json": {"schema": {
661
+ "type": "object",
662
+ "properties": {
663
+ "label": {"type": "string", "default": "Finding"},
664
+ "properties": {"type": "object", "properties": {
665
+ "name": {"type": "string"},
666
+ "content": {"type": "string"},
667
+ }, "required": ["name"]},
668
+ },
669
+ "required": ["label", "properties"],
670
+ }}}},
671
+ "responses": {"200": {"description": "Created node"}},
672
+ }
673
+ },
674
+ "/sessions/{session_id}/dispatch": {
675
+ "post": {
676
+ "operationId": "dispatchGoal",
677
+ "summary": "Decompose a goal into tasks and assign to agents",
678
+ "parameters": [{"name": "session_id", "in": "path", "required": True, "schema": {"type": "string"}}],
679
+ "requestBody": {"required": True, "content": {"application/json": {"schema": {
680
+ "type": "object",
681
+ "properties": {"goal": {"type": "string"}},
682
+ "required": ["goal"],
683
+ }}}},
684
+ "responses": {"200": {"description": "Tasks created"}},
685
+ }
686
+ },
687
+ "/sessions/{session_id}/graph/query": {
688
+ "post": {
689
+ "operationId": "queryGraph",
690
+ "summary": "Execute an AIQL query",
691
+ "parameters": [{"name": "session_id", "in": "path", "required": True, "schema": {"type": "string"}}],
692
+ "requestBody": {"required": True, "content": {"application/json": {"schema": {
693
+ "type": "object",
694
+ "properties": {"aiql": {"type": "string"}},
695
+ "required": ["aiql"],
696
+ }}}},
697
+ "responses": {"200": {"description": "Query results"}},
698
+ }
699
+ },
700
+ },
701
+ }
702
+
703
+ # ── Goal Dispatch ─────────────────────────────────────────────
704
+
705
+ class DispatchGoalRequest(BaseModel):
706
+ goal: str
707
+ session_id: str = ""
708
+
709
+ @router.post("/sessions/{session_id}/dispatch")
710
+ async def dispatch_agent_goal(
711
+ session_id: str, req: DispatchGoalRequest, agent=Depends(require_agent),
712
+ ):
713
+ """Decompose a goal into tasks and auto-assign to agents."""
714
+ from ..agents.dispatcher import dispatch_goal as _dispatch
715
+ from ..context.store_factory import create_agent_registry
716
+ from ..context.session import ContextSessionManager
717
+
718
+ llm_fn = None
719
+ try:
720
+ from ..llm import get_llm_client
721
+ llm = get_llm_client()
722
+ llm_fn = lambda prompt: llm.generate(prompt=prompt, max_tokens=500)
723
+ except Exception:
724
+ pass
725
+
726
+ result = _dispatch(
727
+ goal=req.goal,
728
+ session_id=session_id,
729
+ graph_registry=graph_registry,
730
+ agent_registry=create_agent_registry(),
731
+ session_manager=ContextSessionManager(),
732
+ llm_fn=llm_fn,
733
+ )
734
+ return result
735
+
736
+ # ── Agent Context ───────────────────────────────────────────────
737
+
738
+ @router.get("/sessions/{session_id}/orient")
739
+ async def orient_agent(
740
+ session_id: str,
741
+ max_tokens: int = Query(2000, description="Max projection budget"),
742
+ max_time_ms: int = Query(1000, description="Max time budget in ms"),
743
+ tier: Optional[str] = Query(None, description="Projection tier: instant|fast|standard|deep. Defaults to platform-appropriate tier."),
744
+ agent=Depends(require_agent),
745
+ ):
746
+ """Contextual Graph Projection — queryless context delivery.
747
+
748
+ Returns task-specific projected context from the shared knowledge graph.
749
+ Tier controls pipeline depth: instant (cache only), fast (BM25),
750
+ standard (BM25+Qdrant+hops), deep (all stages+signals+extended).
751
+ """
752
+ session = _get_session_or_404(session_id)
753
+ conn = _get_connection(session)
754
+ rt_conn = _get_runtime_connection(session)
755
+ pg = _get_project_graph(session)
756
+
757
+ from ..tools.registry import ToolContext
758
+ ctx = ToolContext(
759
+ conn=conn,
760
+ runtime_conn=rt_conn,
761
+ agent_id=agent.agent_id,
762
+ agent_name=agent.name,
763
+ )
764
+ ctx.project = pg
765
+
766
+ tier_was_explicit = tier is not None
767
+ tier = _tier_for_platform(agent.platform, tier)
768
+ hints = _build_platform_hints(agent.platform, tier, tier_was_explicit)
769
+
770
+ # Try CGP projection with budget
771
+ try:
772
+ from ..context.projection import project_context
773
+ projection = project_context(ctx, max_tokens=max_tokens,
774
+ max_time_ms=max_time_ms, tier=tier)
775
+ if projection:
776
+ return {"orient": projection, "mode": "projection", "agent": agent.name,
777
+ "tier": tier, "platform_hints": hints}
778
+ except Exception:
779
+ pass
780
+
781
+ # Fallback: classic graph summary
782
+ try:
783
+ from ..tools.graph import _orient
784
+ result = _orient(ctx)
785
+ return {"orient": result, "mode": "manifest", "agent": agent.name,
786
+ "tier": "fallback", "platform_hints": hints}
787
+ except Exception as e:
788
+ return {"orient": f"Graph: {session.graph_namespace}", "mode": "basic",
789
+ "agent": agent.name, "tier": "fallback", "platform_hints": hints}
790
+
791
+ @router.get("/sessions/{session_id}/context")
792
+ async def get_agent_context(
793
+ session_id: str,
794
+ system_prompt: str = Query("", description="Custom system prompt"),
795
+ max_tokens: int = Query(6000, description="Max token budget"),
796
+ agent=Depends(require_agent),
797
+ ):
798
+ """Get full project context for this agent — requirements, tasks, decisions, code produced.
799
+
800
+ This is the main way an agent understands what it should do and what's happened so far.
801
+ Returns LLM-ready context as a single prompt string.
802
+ """
803
+ session = _get_session_or_404(session_id)
804
+ pg = _get_project_graph(session)
805
+ context = pg.build_agent_context(
806
+ agent_id=agent.agent_id,
807
+ system_prompt=system_prompt,
808
+ max_tokens=max_tokens,
809
+ )
810
+ return {"context": context, "session": session.name, "agent": agent.name}
811
+
812
+ # ── Natural Language Query ──────────────────────────────────────
813
+
814
+ class AskRequest(BaseModel):
815
+ question: str
816
+
817
+ @router.post("/sessions/{session_id}/graph/ask")
818
+ async def ask_graph(session_id: str, req: AskRequest, agent=Depends(require_agent)):
819
+ """Ask about the project in plain English. No query syntax needed.
820
+
821
+ Examples: "What are the requirements?", "What has codex done?",
822
+ "Show me the decisions", "What tasks are blocked?"
823
+ """
824
+ session = _get_session_or_404(session_id)
825
+ conn = _get_connection(session)
826
+ pg = _get_project_graph(session)
827
+
828
+ from ..search.semantic_query import semantic_search
829
+ result = semantic_search(req.question, conn, pg)
830
+ return result
831
+
832
+ # ── Task Context ────────────────────────────────────────────────
833
+
834
+ @router.get("/sessions/{session_id}/tasks/{task_id}/context")
835
+ async def get_task_context(session_id: str, task_id: str, agent=Depends(require_agent)):
836
+ """Get everything about a specific task: requirements, decisions, code files, dependencies."""
837
+ session = _get_session_or_404(session_id)
838
+ pg = _get_project_graph(session)
839
+ conn = _get_connection(session)
840
+
841
+ node = pg._resolve_task_id(task_id)
842
+ if not node:
843
+ raise HTTPException(404, f"Task {task_id} not found")
844
+
845
+ props = node.properties if hasattr(node, "properties") else {}
846
+ tid = node.id if hasattr(node, "id") else task_id
847
+
848
+ # Find related nodes via edges
849
+ edges = conn.get_edges()
850
+ related = {"requirements": [], "decisions": [], "code_files": [], "subtasks": [], "depends_on": []}
851
+
852
+ for e in edges:
853
+ src = e.source if hasattr(e, "source") else e.get("source", "")
854
+ tgt = e.target if hasattr(e, "target") else e.get("target", "")
855
+ lbl = e.label if hasattr(e, "label") else e.get("label", "")
856
+
857
+ if src == tid or tgt == tid:
858
+ other_id = tgt if src == tid else src
859
+ other = conn.get_node(other_id)
860
+ if other:
861
+ other_label = other.label if hasattr(other, "label") else ""
862
+ other_props = other.properties if hasattr(other, "properties") else {}
863
+ info = {"id": other_id, "label": other_label, **other_props}
864
+
865
+ if other_label == "Requirement":
866
+ related["requirements"].append(info)
867
+ elif other_label == "Decision":
868
+ related["decisions"].append(info)
869
+ elif other_label == "CodeFile":
870
+ related["code_files"].append(info)
871
+ elif other_label == "Task" and lbl == "HAS_SUBTASK":
872
+ related["subtasks"].append(info)
873
+ elif other_label == "Task" and lbl == "DEPENDS_ON":
874
+ related["depends_on"].append(info)
875
+
876
+ return {
877
+ "task": {"id": tid, "label": "Task", **props},
878
+ **related,
879
+ }
880
+
881
+ # ── Promotion ──────────────────────────────────────────────────
882
+
883
+ class PromotionConfigRequest(BaseModel):
884
+ auto_promote_threshold: Optional[float] = None
885
+ review_threshold: Optional[float] = None
886
+ require_human_review: Optional[bool] = None
887
+ weights: Optional[Dict[str, float]] = None
888
+
889
+ class PromotionRejectRequest(BaseModel):
890
+ reason: str = ""
891
+
892
+ @router.get("/sessions/{session_id}/promotion/queue")
893
+ async def promotion_queue(session_id: str, limit: int = Query(50),
894
+ agent=Depends(require_agent)):
895
+ """Get pending promotion review items."""
896
+ session = _get_session_or_404(session_id)
897
+ from ..context.promotion import PromotionQueue
898
+ queue = PromotionQueue(session.graph_namespace)
899
+ items = queue.pending(limit=limit)
900
+ return {"items": items, "count": queue.count()}
901
+
902
+ @router.post("/sessions/{session_id}/promotion/{node_id}/approve")
903
+ async def approve_promotion(session_id: str, node_id: str,
904
+ agent=Depends(require_agent)):
905
+ """Approve a node for promotion to atomic context."""
906
+ session = _get_session_or_404(session_id)
907
+ from ..context.promotion import PromotionQueue, promote_node
908
+
909
+ queue = PromotionQueue(session.graph_namespace)
910
+ queue.approve(node_id, agent.agent_id)
911
+
912
+ rt_conn = _get_runtime_connection(session)
913
+ at_conn = _get_connection(session)
914
+ promoted_id = promote_node(node_id, rt_conn, at_conn,
915
+ promoted_by=agent.name)
916
+
917
+ if promoted_id:
918
+ return {"promoted": True, "atomic_node_id": promoted_id,
919
+ "node_id": node_id}
920
+ raise HTTPException(400, f"Failed to promote node {node_id}")
921
+
922
+ @router.post("/sessions/{session_id}/promotion/{node_id}/reject")
923
+ async def reject_promotion(session_id: str, node_id: str,
924
+ req: PromotionRejectRequest,
925
+ agent=Depends(require_agent)):
926
+ """Reject a node from promotion queue."""
927
+ session = _get_session_or_404(session_id)
928
+ from ..context.promotion import PromotionQueue
929
+
930
+ queue = PromotionQueue(session.graph_namespace)
931
+ queue.reject(node_id, agent.agent_id, reason=req.reason)
932
+ return {"rejected": True, "node_id": node_id}
933
+
934
+ @router.get("/sessions/{session_id}/promotion/config")
935
+ async def get_promotion_config(session_id: str,
936
+ agent=Depends(require_agent)):
937
+ """Get promotion configuration for this session."""
938
+ session = _get_session_or_404(session_id)
939
+ from ..context.promotion import PROMOTION_DEFAULTS
940
+ config = dict(PROMOTION_DEFAULTS)
941
+ session_promo = (session.config or {}).get("promotion", {})
942
+ config.update(session_promo)
943
+ return {"config": config, "session_id": session_id}
944
+
945
+ @router.put("/sessions/{session_id}/promotion/config")
946
+ async def update_promotion_config(session_id: str,
947
+ req: PromotionConfigRequest,
948
+ agent=Depends(require_agent)):
949
+ """Update promotion configuration for this session."""
950
+ session = _get_session_or_404(session_id)
951
+ promo_config = (session.config or {}).get("promotion", {})
952
+
953
+ if req.auto_promote_threshold is not None:
954
+ promo_config["auto_promote_threshold"] = req.auto_promote_threshold
955
+ if req.review_threshold is not None:
956
+ promo_config["review_threshold"] = req.review_threshold
957
+ if req.require_human_review is not None:
958
+ promo_config["require_human_review"] = req.require_human_review
959
+ if req.weights is not None:
960
+ promo_config["weights"] = req.weights
961
+
962
+ config = dict(session.config or {})
963
+ config["promotion"] = promo_config
964
+ try:
965
+ import sqlite3, json as _json
966
+ conn = sqlite3.connect(str(session_manager.db_path))
967
+ conn.execute(
968
+ "UPDATE context_sessions SET config = ? WHERE session_id = ?",
969
+ (_json.dumps(config), session.session_id),
970
+ )
971
+ conn.commit()
972
+ conn.close()
973
+ except Exception as e:
974
+ raise HTTPException(500, f"Failed to save config: {e}")
975
+
976
+ return {"updated": True, "config": promo_config}
977
+
978
+ return router