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.
- contextsynapse/__init__.py +107 -0
- contextsynapse/__main__.py +9 -0
- contextsynapse/a2a/__init__.py +28 -0
- contextsynapse/a2a/client.py +120 -0
- contextsynapse/a2a/discovery.py +128 -0
- contextsynapse/a2a/handlers.py +164 -0
- contextsynapse/a2a/models.py +246 -0
- contextsynapse/a2a/streaming.py +92 -0
- contextsynapse/a2a/task_manager.py +275 -0
- contextsynapse/adapters/__init__.py +98 -0
- contextsynapse/adapters/_base.py +298 -0
- contextsynapse/adapters/autogen/__init__.py +4 -0
- contextsynapse/adapters/autogen/tools.py +34 -0
- contextsynapse/adapters/crewai/__init__.py +4 -0
- contextsynapse/adapters/crewai/tools.py +63 -0
- contextsynapse/adapters/langchain/__init__.py +19 -0
- contextsynapse/adapters/langchain/message_history.py +17 -0
- contextsynapse/adapters/langchain/retriever.py +158 -0
- contextsynapse/adapters/langchain/tools.py +43 -0
- contextsynapse/adapters/langgraph/__init__.py +21 -0
- contextsynapse/adapters/langgraph/checkpoint.py +223 -0
- contextsynapse/adapters/langgraph/context_tools.py +341 -0
- contextsynapse/adapters/langgraph/message_history.py +138 -0
- contextsynapse/adapters/langgraph/tools.py +43 -0
- contextsynapse/adapters/llamaindex/__init__.py +4 -0
- contextsynapse/adapters/llamaindex/tools.py +64 -0
- contextsynapse/adapters/openai/__init__.py +24 -0
- contextsynapse/adapters/openai/tools.py +206 -0
- contextsynapse/adapters/pydantic_ai/__init__.py +4 -0
- contextsynapse/adapters/pydantic_ai/tools.py +36 -0
- contextsynapse/adapters/swarm/__init__.py +4 -0
- contextsynapse/adapters/swarm/tools.py +44 -0
- contextsynapse/agents/__init__.py +6 -0
- contextsynapse/agents/auto_capture.py +80 -0
- contextsynapse/agents/dispatcher.py +299 -0
- contextsynapse/agents/notifications.py +112 -0
- contextsynapse/aiql/__init__.py +71 -0
- contextsynapse/aiql/cache.py +341 -0
- contextsynapse/aiql/compiler/__init__.py +62 -0
- contextsynapse/aiql/compiler/optimizer.py +126 -0
- contextsynapse/aiql/compiler/planner.py +366 -0
- contextsynapse/aiql/compiler/statistics.py +235 -0
- contextsynapse/aiql/compiler/validator.py +425 -0
- contextsynapse/aiql/engine/__init__.py +11 -0
- contextsynapse/aiql/engine/chunk_storage.py +2584 -0
- contextsynapse/aiql/engine/executor.py +7737 -0
- contextsynapse/aiql/engine/extraction_integration.py +442 -0
- contextsynapse/aiql/engine/graph_builder.py +405 -0
- contextsynapse/aiql/engine/node_creation_from_normalized.py +691 -0
- contextsynapse/aiql/engine/performance_monitor.py +289 -0
- contextsynapse/aiql/engine/postprocessing.py +365 -0
- contextsynapse/aiql/engine/ray_stage_executors.py +918 -0
- contextsynapse/aiql/engine/redis_cache.py +106 -0
- contextsynapse/aiql/engine/streaming_json.py +366 -0
- contextsynapse/aiql/engine/streaming_parquet.py +568 -0
- contextsynapse/aiql/engine/streaming_ray_chunking.py +419 -0
- contextsynapse/aiql/grammar/__init__.py +14 -0
- contextsynapse/aiql/grammar/aiql_grammar.py +1822 -0
- contextsynapse/aiql/grammar/base.py +71 -0
- contextsynapse/aiql/parser/__init__.py +46 -0
- contextsynapse/aiql/parser/aiql_parser.py +11330 -0
- contextsynapse/aiql/parser/base_parser.py +47 -0
- contextsynapse/api/__init__.py +5 -0
- contextsynapse/api/a2a_router.py +233 -0
- contextsynapse/api/admin_router.py +140 -0
- contextsynapse/api/agent_worker_router.py +978 -0
- contextsynapse/api/algorithms_router.py +317 -0
- contextsynapse/api/api.py +4144 -0
- contextsynapse/api/audit.py +204 -0
- contextsynapse/api/auth.py +441 -0
- contextsynapse/api/auth_router.py +537 -0
- contextsynapse/api/billing.py +261 -0
- contextsynapse/api/billing_router.py +275 -0
- contextsynapse/api/boundary_router.py +452 -0
- contextsynapse/api/cognition_router.py +116 -0
- contextsynapse/api/connector_router.py +237 -0
- contextsynapse/api/context_router.py +1619 -0
- contextsynapse/api/dashboard_router.py +11367 -0
- contextsynapse/api/errors.py +183 -0
- contextsynapse/api/events.py +88 -0
- contextsynapse/api/experiment_bootstrap.py +321 -0
- contextsynapse/api/experiment_orchestrator.py +1078 -0
- contextsynapse/api/experiment_router.py +2089 -0
- contextsynapse/api/feature_gate.py +89 -0
- contextsynapse/api/federation_router.py +108 -0
- contextsynapse/api/integrations.py +567 -0
- contextsynapse/api/integrations_router.py +461 -0
- contextsynapse/api/intelligence_router.py +1028 -0
- contextsynapse/api/metering.py +155 -0
- contextsynapse/api/metrics.py +112 -0
- contextsynapse/api/models.py +38 -0
- contextsynapse/api/monitoring_router.py +174 -0
- contextsynapse/api/pipeline_router.py +1437 -0
- contextsynapse/api/playground_conn.py +116 -0
- contextsynapse/api/projects_router.py +131 -0
- contextsynapse/api/queries_router.py +282 -0
- contextsynapse/api/quota.py +95 -0
- contextsynapse/api/rate_limit.py +64 -0
- contextsynapse/api/search_router.py +261 -0
- contextsynapse/api/shield_router.py +447 -0
- contextsynapse/api/tenant_guard.py +103 -0
- contextsynapse/api/tenant_quotas.py +231 -0
- contextsynapse/api/tenants.py +305 -0
- contextsynapse/api/users.py +646 -0
- contextsynapse/api/v1.py +167 -0
- contextsynapse/api/vertical_builder_router.py +387 -0
- contextsynapse/benchmark/__init__.py +15 -0
- contextsynapse/benchmark/__main__.py +64 -0
- contextsynapse/benchmark/agents.py +338 -0
- contextsynapse/benchmark/live_ab.py +338 -0
- contextsynapse/benchmark/reporter.py +149 -0
- contextsynapse/benchmark/runner.py +264 -0
- contextsynapse/benchmark/savings.py +266 -0
- contextsynapse/benchmark/token_counter.py +212 -0
- contextsynapse/benchmark/workloads.py +289 -0
- contextsynapse/buffer/__init__.py +154 -0
- contextsynapse/buffer/base.py +257 -0
- contextsynapse/buffer/duckdb_buffer.py +246 -0
- contextsynapse/buffer/file_buffer.py +229 -0
- contextsynapse/buffer/lmdb_buffer.py +286 -0
- contextsynapse/buffer/local_buffer.py +154 -0
- contextsynapse/buffer/redis_buffer.py +211 -0
- contextsynapse/buffer/sqlite_buffer.py +266 -0
- contextsynapse/cache.py +2 -0
- contextsynapse/cli/__init__.py +40 -0
- contextsynapse/cli/cli.py +475 -0
- contextsynapse/cli/config_cli.py +177 -0
- contextsynapse/cli/scaffold.py +465 -0
- contextsynapse/cognition/__init__.py +31 -0
- contextsynapse/cognition/derivation.py +55 -0
- contextsynapse/cognition/events.py +135 -0
- contextsynapse/cognition/feedback.py +52 -0
- contextsynapse/cognition/hallucination.py +90 -0
- contextsynapse/cognition/invalidation.py +70 -0
- contextsynapse/cognition/read_tracker.py +50 -0
- contextsynapse/collaboration.py +2 -0
- contextsynapse/comparison.py +2 -0
- contextsynapse/config/__init__.py +10 -0
- contextsynapse/config/connectors/amfi_nav.yaml +18 -0
- contextsynapse/config/connectors/auto_sector.yaml +20 -0
- contextsynapse/config/connectors/banking_sector.yaml +20 -0
- contextsynapse/config/connectors/energy_sector.yaml +20 -0
- contextsynapse/config/connectors/fmcg_sector.yaml +20 -0
- contextsynapse/config/connectors/nsdl_fpi.yaml +9 -0
- contextsynapse/config/connectors/nse_price.yaml +61 -0
- contextsynapse/config/connectors/pharma_sector.yaml +20 -0
- contextsynapse/config/connectors/rbi_dbie.yaml +12 -0
- contextsynapse/config/context_presets/ai_research.json +25 -0
- contextsynapse/config/context_presets/chatgpt_conversations.json +19 -0
- contextsynapse/config/context_presets/claude_conversations.json +19 -0
- contextsynapse/config/context_presets/legal_compliance.json +21 -0
- contextsynapse/config/context_presets/meeting_notes.json +21 -0
- contextsynapse/config/context_presets/slack_engineering.json +21 -0
- contextsynapse/config/context_presets/tech_climate.json +24 -0
- contextsynapse/config/context_presets/toi_politics.json +25 -0
- contextsynapse/config/database_config.py +453 -0
- contextsynapse/config/deployment.py +221 -0
- contextsynapse/config/gpt_action_schema.json +164 -0
- contextsynapse/config/indicators/india_macro.yaml +213 -0
- contextsynapse/config/logging_config.py +88 -0
- contextsynapse/config/mode_config.py +223 -0
- contextsynapse/config/schemas/api_spec.yaml +48 -0
- contextsynapse/config/schemas/architecture_doc.yaml +51 -0
- contextsynapse/config/schemas/chat_history.yaml +90 -0
- contextsynapse/config/schemas/chatgpt_conversation.yaml +101 -0
- contextsynapse/config/schemas/claude_conversation.yaml +148 -0
- contextsynapse/config/schemas/contract.yaml +48 -0
- contextsynapse/config/schemas/conversation.yaml +167 -0
- contextsynapse/config/schemas/database.yaml +74 -0
- contextsynapse/config/schemas/decision.yaml +72 -0
- contextsynapse/config/schemas/ecommerce.yaml +56 -0
- contextsynapse/config/schemas/education.yaml +42 -0
- contextsynapse/config/schemas/healthcare.yaml +133 -0
- contextsynapse/config/schemas/hr_document.yaml +48 -0
- contextsynapse/config/schemas/invoice.yaml +40 -0
- contextsynapse/config/schemas/knowledge_base.yaml +75 -0
- contextsynapse/config/schemas/legal.yaml +42 -0
- contextsynapse/config/schemas/marketing_content.yaml +48 -0
- contextsynapse/config/schemas/meeting_notes.yaml +51 -0
- contextsynapse/config/schemas/news_article.yaml +61 -0
- contextsynapse/config/schemas/real_estate.yaml +49 -0
- contextsynapse/config/schemas/requirements_doc.yaml +48 -0
- contextsynapse/config/schemas/research_paper.yaml +56 -0
- contextsynapse/config/schemas/resume.yaml +49 -0
- contextsynapse/config/schemas/retail.yaml +42 -0
- contextsynapse/config/schemas/rule_meta.yaml +173 -0
- contextsynapse/config/schemas/rules.yaml +72 -0
- contextsynapse/config/schemas/screener_meta.yaml +236 -0
- contextsynapse/config/schemas/sdlc.yaml +103 -0
- contextsynapse/config/schemas/session_graph.yaml +172 -0
- contextsynapse/config/schemas/support_ticket.yaml +48 -0
- contextsynapse/config/schemas/web.yaml +64 -0
- contextsynapse/config/sensors/earnings_calendar.yaml +52 -0
- contextsynapse/config/sensors/global_risk.yaml +53 -0
- contextsynapse/config/sensors/government_policy.yaml +54 -0
- contextsynapse/config/sensors/supply_chain.yaml +50 -0
- contextsynapse/config/sensors/templates/behavioral.yaml +56 -0
- contextsynapse/config/sensors/templates/equity_sentiment.yaml +48 -0
- contextsynapse/config/sensors/templates/global_macro.yaml +47 -0
- contextsynapse/config/sensors/templates/market_data.yaml +118 -0
- contextsynapse/config/sensors/templates/sector_technical.yaml +47 -0
- contextsynapse/config/sensors/weather.yaml +52 -0
- contextsynapse/config/storage_strategy.py +202 -0
- contextsynapse/config/templates/competitive_intel.yaml +39 -0
- contextsynapse/config/templates/geopolitical_risk.yaml +46 -0
- contextsynapse/config/templates/market_analysis.yaml +58 -0
- contextsynapse/connectors/__init__.py +15 -0
- contextsynapse/connectors/api_router.py +121 -0
- contextsynapse/connectors/base.py +357 -0
- contextsynapse/connectors/financial/audio.py +170 -0
- contextsynapse/connectors/financial/youtube.py +272 -0
- contextsynapse/connectors/github.py +164 -0
- contextsynapse/connectors/jira.py +120 -0
- contextsynapse/connectors/kafka_connector.py +124 -0
- contextsynapse/connectors/pipeline_connector.py +747 -0
- contextsynapse/connectors/registry.py +116 -0
- contextsynapse/connectors/scheduler.py +87 -0
- contextsynapse/connectors/universal.py +360 -0
- contextsynapse/context/__init__.py +96 -0
- contextsynapse/context/__main__.py +4 -0
- contextsynapse/context/acl.py +125 -0
- contextsynapse/context/agent_card.py +201 -0
- contextsynapse/context/agent_memory.py +814 -0
- contextsynapse/context/agents.py +472 -0
- contextsynapse/context/agents_redis.py +391 -0
- contextsynapse/context/assembled.py +204 -0
- contextsynapse/context/attribution.py +113 -0
- contextsynapse/context/blob.py +184 -0
- contextsynapse/context/boundaries.py +187 -0
- contextsynapse/context/boundary.py +507 -0
- contextsynapse/context/bundle.py +155 -0
- contextsynapse/context/cli.py +519 -0
- contextsynapse/context/collaboration.py +165 -0
- contextsynapse/context/compiler.py +604 -0
- contextsynapse/context/composite.py +536 -0
- contextsynapse/context/compression.py +147 -0
- contextsynapse/context/context_manager.py +888 -0
- contextsynapse/context/context_schema.py +90 -0
- contextsynapse/context/context_schema.yaml +105 -0
- contextsynapse/context/context_units.py +891 -0
- contextsynapse/context/conversation.py +488 -0
- contextsynapse/context/dedup.py +207 -0
- contextsynapse/context/document_processor.py +698 -0
- contextsynapse/context/embedding_hooks.py +338 -0
- contextsynapse/context/execution_schema.py +274 -0
- contextsynapse/context/fan_out.py +138 -0
- contextsynapse/context/frozen.py +80 -0
- contextsynapse/context/gravity.py +227 -0
- contextsynapse/context/hub.py +1401 -0
- contextsynapse/context/ingest.py +391 -0
- contextsynapse/context/injection.py +226 -0
- contextsynapse/context/interaction_graphifier.py +562 -0
- contextsynapse/context/layers.py +574 -0
- contextsynapse/context/projection.py +1195 -0
- contextsynapse/context/promotion.py +634 -0
- contextsynapse/context/proof.py +131 -0
- contextsynapse/context/propagation.py +577 -0
- contextsynapse/context/pubsub.py +135 -0
- contextsynapse/context/quality.py +1054 -0
- contextsynapse/context/quality_tracker.py +270 -0
- contextsynapse/context/scoping.py +370 -0
- contextsynapse/context/seed.py +992 -0
- contextsynapse/context/session.py +1142 -0
- contextsynapse/context/session_graph.py +593 -0
- contextsynapse/context/session_memory.py +309 -0
- contextsynapse/context/session_resolver.py +111 -0
- contextsynapse/context/skill_learning.py +237 -0
- contextsynapse/context/smart_budget.py +129 -0
- contextsynapse/context/source_policy.py +242 -0
- contextsynapse/context/store_factory.py +53 -0
- contextsynapse/context/sync.py +648 -0
- contextsynapse/context/token.py +236 -0
- contextsynapse/context/vector_integration.py +297 -0
- contextsynapse/context/webhooks.py +194 -0
- contextsynapse/context/working_memory.py +224 -0
- contextsynapse/core/__init__.py +18 -0
- contextsynapse/core/archiver.py +237 -0
- contextsynapse/core/change_stream.py +282 -0
- contextsynapse/core/checkpoint.py +310 -0
- contextsynapse/core/cloud_storage.py +387 -0
- contextsynapse/core/context_state.py +651 -0
- contextsynapse/core/cron.py +324 -0
- contextsynapse/core/datasource.py +643 -0
- contextsynapse/core/db.py +289 -0
- contextsynapse/core/federation.py +283 -0
- contextsynapse/core/graph_coordinator.py +201 -0
- contextsynapse/core/graph_intelligence.py +388 -0
- contextsynapse/core/graph_structures.py +106 -0
- contextsynapse/core/graph_sync.py +157 -0
- contextsynapse/core/hybrid_graph_storage.py +2998 -0
- contextsynapse/core/lifecycle.py +691 -0
- contextsynapse/core/multiworker.py +378 -0
- contextsynapse/core/pruning.py +312 -0
- contextsynapse/core/redis_registry.py +524 -0
- contextsynapse/core/registry.py +1043 -0
- contextsynapse/core/registry_factory.py +51 -0
- contextsynapse/core/registry_metadata.py +294 -0
- contextsynapse/core/replay.py +354 -0
- contextsynapse/core/replication.py +339 -0
- contextsynapse/core/time_travel.py +260 -0
- contextsynapse/core/write_behind.py +152 -0
- contextsynapse/core/write_context.py +39 -0
- contextsynapse/db/__init__.py +1 -0
- contextsynapse/db/postgres.py +267 -0
- contextsynapse/db/profile.py +236 -0
- contextsynapse/db/rules.py +328 -0
- contextsynapse/demo/__init__.py +0 -0
- contextsynapse/demo/report.py +147 -0
- contextsynapse/demo/runner.py +520 -0
- contextsynapse/demo/scenario.py +165 -0
- contextsynapse/demo/session_demo.py +280 -0
- contextsynapse/demo/session_report.py +143 -0
- contextsynapse/demo/universal_runner.py +257 -0
- contextsynapse/engine/__init__.py +15 -0
- contextsynapse/engine/core.py +555 -0
- contextsynapse/extraction/__init__.py +69 -0
- contextsynapse/extraction/cli.py +274 -0
- contextsynapse/extraction/config.py +107 -0
- contextsynapse/extraction/examples/example_usage.py +118 -0
- contextsynapse/extraction/examples/sample_manifest.json +53 -0
- contextsynapse/extraction/examples/sample_normalized.json +185 -0
- contextsynapse/extraction/extract_engine.py +440 -0
- contextsynapse/extraction/extractors/__init__.py +37 -0
- contextsynapse/extraction/extractors/chat_export_extractor.py +189 -0
- contextsynapse/extraction/extractors/csv_extractor.py +83 -0
- contextsynapse/extraction/extractors/docx_extractor.py +129 -0
- contextsynapse/extraction/extractors/excel_extractor.py +152 -0
- contextsynapse/extraction/extractors/html_extractor.py +146 -0
- contextsynapse/extraction/extractors/pdf_extractor.py +635 -0
- contextsynapse/extraction/extractors/pdf_extractor_layoutparser.py +408 -0
- contextsynapse/extraction/extractors/register_extractors.py +63 -0
- contextsynapse/extraction/extractors/text_extractor.py +65 -0
- contextsynapse/extraction/extractors/txt_extractor.py +120 -0
- contextsynapse/extraction/extractors/website_extractor.py +246 -0
- contextsynapse/extraction/fact_extractor.py +236 -0
- contextsynapse/extraction/file_manager.py +353 -0
- contextsynapse/extraction/hierarchy.py +239 -0
- contextsynapse/extraction/id_generator.py +450 -0
- contextsynapse/extraction/layout_detector.py +409 -0
- contextsynapse/extraction/llm_entity_extractor.py +301 -0
- contextsynapse/extraction/normalized_store.py +480 -0
- contextsynapse/extraction/normalizer.py +594 -0
- contextsynapse/extraction/parquet_writer.py +598 -0
- contextsynapse/extraction/post_processors/__init__.py +40 -0
- contextsynapse/extraction/post_processors/column_reorganizer.py +507 -0
- contextsynapse/extraction/ray_normalization.py +233 -0
- contextsynapse/extraction/ray_runner.py +207 -0
- contextsynapse/extraction/redis_schema_registry.py +222 -0
- contextsynapse/extraction/registry.py +204 -0
- contextsynapse/extraction/schema_loader.py +663 -0
- contextsynapse/extraction/schema_registry.py +230 -0
- contextsynapse/extraction/semantic_normalizer.py +245 -0
- contextsynapse/extraction/version_manager.py +314 -0
- contextsynapse/gateway/__init__.py +24 -0
- contextsynapse/gateway/action_emitter.py +212 -0
- contextsynapse/gateway/cost_tracker.py +508 -0
- contextsynapse/gateway/gateway.py +377 -0
- contextsynapse/gateway/policy.py +303 -0
- contextsynapse/governance/__init__.py +13 -0
- contextsynapse/governance/layer.py +513 -0
- contextsynapse/ingestion/__init__.py +15 -0
- contextsynapse/ingestion/amplifier.py +288 -0
- contextsynapse/ingestion/async_ingest.py +463 -0
- contextsynapse/ingestion/chunker.py +303 -0
- contextsynapse/ingestion/chunking.py +58 -0
- contextsynapse/ingestion/cleaner.py +190 -0
- contextsynapse/ingestion/cleanse.py +252 -0
- contextsynapse/ingestion/connectors/__init__.py +38 -0
- contextsynapse/ingestion/connectors/base.py +300 -0
- contextsynapse/ingestion/connectors/chatgpt_memory.py +247 -0
- contextsynapse/ingestion/connectors/claude_memory.py +274 -0
- contextsynapse/ingestion/connectors/sap_ingestor.py +208 -0
- contextsynapse/ingestion/content_detector.py +80 -0
- contextsynapse/ingestion/content_monitor.py +502 -0
- contextsynapse/ingestion/dedup.py +106 -0
- contextsynapse/ingestion/feed.py +640 -0
- contextsynapse/ingestion/filters.py +461 -0
- contextsynapse/ingestion/graph_builder.py +705 -0
- contextsynapse/ingestion/input_classifier.py +307 -0
- contextsynapse/ingestion/job_manager.py +571 -0
- contextsynapse/ingestion/llm_extractor.py +610 -0
- contextsynapse/ingestion/orchestrator.py +116 -0
- contextsynapse/ingestion/parsers/__init__.py +19 -0
- contextsynapse/ingestion/parsers/graph_parser_registry.py +103 -0
- contextsynapse/ingestion/parsers/graphml_parser.py +141 -0
- contextsynapse/ingestion/parsers/jsonld_parser.py +171 -0
- contextsynapse/ingestion/parsers/rdf_parser.py +150 -0
- contextsynapse/ingestion/pipeline_context.py +228 -0
- contextsynapse/ingestion/pipeline_store.py +1195 -0
- contextsynapse/ingestion/queue.py +133 -0
- contextsynapse/ingestion/reprocess.py +179 -0
- contextsynapse/ingestion/scenario_router.py +224 -0
- contextsynapse/ingestion/schema_extractor.py +759 -0
- contextsynapse/ingestion/schema_validator.py +274 -0
- contextsynapse/ingestion/sdlc_ingest.py +87 -0
- contextsynapse/ingestion/smart_ingest.py +2451 -0
- contextsynapse/ingestion/source_connector.py +171 -0
- contextsynapse/ingestion/stage_executor.py +2095 -0
- contextsynapse/ingestion/stages/__init__.py +9 -0
- contextsynapse/ingestion/stages/tabular_to_graph.py +289 -0
- contextsynapse/ingestion/strategies.py +386 -0
- contextsynapse/ingestion/universal/__init__.py +4 -0
- contextsynapse/ingestion/universal/_operator_registry.py +59 -0
- contextsynapse/ingestion/universal/ingest_content.py +32 -0
- contextsynapse/ingestion/universal/operators/__init__.py +1 -0
- contextsynapse/ingestion/universal/operators/base.py +19 -0
- contextsynapse/ingestion/universal/operators/build_edges.py +75 -0
- contextsynapse/ingestion/universal/operators/cluster_topics.py +397 -0
- contextsynapse/ingestion/universal/operators/deduplicate.py +45 -0
- contextsynapse/ingestion/universal/operators/detect_signals.py +90 -0
- contextsynapse/ingestion/universal/operators/embed.py +80 -0
- contextsynapse/ingestion/universal/operators/extract_entities.py +253 -0
- contextsynapse/ingestion/universal/operators/extract_preferences.py +117 -0
- contextsynapse/ingestion/universal/operators/filter_content.py +69 -0
- contextsynapse/ingestion/universal/operators/index_bm25.py +52 -0
- contextsynapse/ingestion/universal/operators/infer_domains.py +69 -0
- contextsynapse/ingestion/universal/operators/link_cross_reference.py +210 -0
- contextsynapse/ingestion/universal/operators/parse_records.py +66 -0
- contextsynapse/ingestion/universal/operators/resolve_entities.py +261 -0
- contextsynapse/ingestion/universal/operators/scanners/__init__.py +16 -0
- contextsynapse/ingestion/universal/operators/scanners/edges.py +173 -0
- contextsynapse/ingestion/universal/operators/scanners/git.py +187 -0
- contextsynapse/ingestion/universal/operators/scanners/github.py +135 -0
- contextsynapse/ingestion/universal/operators/scanners/github_api.py +509 -0
- contextsynapse/ingestion/universal/operators/scanners/helpers.py +67 -0
- contextsynapse/ingestion/universal/operators/scanners/jira.py +65 -0
- contextsynapse/ingestion/universal/operators/scanners/llm_enrichment.py +82 -0
- contextsynapse/ingestion/universal/operators/scanners/repo.py +149 -0
- contextsynapse/ingestion/universal/operators/sdlc_scan.py +213 -0
- contextsynapse/ingestion/universal/operators/spec_scanner.py +149 -0
- contextsynapse/ingestion/universal/operators/store_documents.py +72 -0
- contextsynapse/ingestion/universal/operators/synthesize_cu.py +180 -0
- contextsynapse/ingestion/universal/operators/validate_gate.py +65 -0
- contextsynapse/ingestion/universal/parsers/__init__.py +5 -0
- contextsynapse/ingestion/universal/parsers/record_parser.py +203 -0
- contextsynapse/ingestion/universal/parsers/turn_parser.py +329 -0
- contextsynapse/ingestion/universal/shared_signals.py +161 -0
- contextsynapse/ingestion/universal/stage_executor.py +219 -0
- contextsynapse/ingestion/web_crawler.py +265 -0
- contextsynapse/intelligence/__init__.py +53 -0
- contextsynapse/intelligence/alerts.py +198 -0
- contextsynapse/intelligence/collector.py +655 -0
- contextsynapse/intelligence/comparison.py +198 -0
- contextsynapse/intelligence/config.py +56 -0
- contextsynapse/intelligence/conflict_detector.py +182 -0
- contextsynapse/intelligence/context_gaps.py +192 -0
- contextsynapse/intelligence/context_radar.py +135 -0
- contextsynapse/intelligence/correlation_engine.py +416 -0
- contextsynapse/intelligence/correlation_template.py +120 -0
- contextsynapse/intelligence/dedup.py +296 -0
- contextsynapse/intelligence/event_bus.py +91 -0
- contextsynapse/intelligence/feedback_loop.py +101 -0
- contextsynapse/intelligence/freshness.py +307 -0
- contextsynapse/intelligence/fundamental_feed.py +551 -0
- contextsynapse/intelligence/geo_data.py +170 -0
- contextsynapse/intelligence/geo_reference.py +112 -0
- contextsynapse/intelligence/graph_fusion.py +465 -0
- contextsynapse/intelligence/heuristics.py +176 -0
- contextsynapse/intelligence/impact_tracker.py +336 -0
- contextsynapse/intelligence/macro_indicators.py +319 -0
- contextsynapse/intelligence/ocr.py +104 -0
- contextsynapse/intelligence/persistence.py +267 -0
- contextsynapse/intelligence/pipeline_health.py +130 -0
- contextsynapse/intelligence/price_feed.py +221 -0
- contextsynapse/intelligence/rate_limiter.py +130 -0
- contextsynapse/intelligence/reactive.py +411 -0
- contextsynapse/intelligence/runtime_context.py +390 -0
- contextsynapse/intelligence/sentiment_decay.py +426 -0
- contextsynapse/intelligence/session.py +277 -0
- contextsynapse/intelligence/signal_hierarchy.py +438 -0
- contextsynapse/intelligence/signals.py +185 -0
- contextsynapse/intelligence/source_watcher.py +132 -0
- contextsynapse/intelligence/table_enricher.py +256 -0
- contextsynapse/intelligence/tagger.py +319 -0
- contextsynapse/intelligence/timeseries.py +209 -0
- contextsynapse/intelligence/tracker.py +318 -0
- contextsynapse/intelligence/watchdog.py +392 -0
- contextsynapse/intelligence/watchdog_manager.py +263 -0
- contextsynapse/intelligence/webhook_receiver.py +165 -0
- contextsynapse/intelligence/weight_learner.py +234 -0
- contextsynapse/llm/__init__.py +8 -0
- contextsynapse/llm/client.py +348 -0
- contextsynapse/llm/rate_limiter.py +166 -0
- contextsynapse/logging_config.py +2 -0
- contextsynapse/marketplace/__init__.py +7 -0
- contextsynapse/marketplace/registry.py +173 -0
- contextsynapse/mcp/__init__.py +11 -0
- contextsynapse/mcp/__main__.py +4 -0
- contextsynapse/mcp/auth_middleware.py +212 -0
- contextsynapse/mcp/connection_pool.py +128 -0
- contextsynapse/mcp/server.py +986 -0
- contextsynapse/mcp/session_context.py +67 -0
- contextsynapse/memory/__init__.py +34 -0
- contextsynapse/memory/engine.py +475 -0
- contextsynapse/memory/temporal.py +327 -0
- contextsynapse/metadata/__init__.py +12 -0
- contextsynapse/metadata/metadata_db.py +272 -0
- contextsynapse/metadata/metadata_query_tool.py +300 -0
- contextsynapse/metadata/metadata_tracker.py +425 -0
- contextsynapse/models/__init__.py +27 -0
- contextsynapse/models/embedding_service.py +360 -0
- contextsynapse/models/model_registry.py +228 -0
- contextsynapse/multiworker.py +2 -0
- contextsynapse/pipelines/__init__.py +1 -0
- contextsynapse/pipelines/executor.py +851 -0
- contextsynapse/pipelines/filters.py +189 -0
- contextsynapse/pipelines/models.py +176 -0
- contextsynapse/pipelines/scheduler.py +268 -0
- contextsynapse/playground/__init__.py +1 -0
- contextsynapse/playground/templates.py +85 -0
- contextsynapse/plugins/__init__.py +43 -0
- contextsynapse/plugins/api.py +66 -0
- contextsynapse/plugins/base.py +350 -0
- contextsynapse/plugins/domain_interface.py +508 -0
- contextsynapse/plugins/domain_loader.py +241 -0
- contextsynapse/plugins/loader.py +148 -0
- contextsynapse/plugins/registry.py +265 -0
- contextsynapse/project/__init__.py +11 -0
- contextsynapse/project/code_context.py +807 -0
- contextsynapse/project/connectors/__init__.py +4 -0
- contextsynapse/project/connectors/github_connector.py +113 -0
- contextsynapse/project/connectors/jira_connector.py +160 -0
- contextsynapse/project/graph.py +1023 -0
- contextsynapse/project/project_context.py +545 -0
- contextsynapse/project/scanner_registry.py +61 -0
- contextsynapse/project/schema_manager.py +322 -0
- contextsynapse/project/schemas/knowledge.yaml +59 -0
- contextsynapse/project/schemas/sdlc.yaml +369 -0
- contextsynapse/project/sdlc_schema.py +172 -0
- contextsynapse/project/spec_parser.py +85 -0
- contextsynapse/project/task_lock.py +181 -0
- contextsynapse/project/task_stream.py +203 -0
- contextsynapse/project/team_tools.py +537 -0
- contextsynapse/py.typed +0 -0
- contextsynapse/realtime/__init__.py +6 -0
- contextsynapse/realtime/collaboration.py +147 -0
- contextsynapse/rules/__init__.py +10 -0
- contextsynapse/rules/engine.py +116 -0
- contextsynapse/rules/evaluators/__init__.py +6 -0
- contextsynapse/rules/evaluators/aggregate.py +205 -0
- contextsynapse/rules/evaluators/custom.py +170 -0
- contextsynapse/rules/evaluators/pre_action.py +125 -0
- contextsynapse/rules/evaluators/temporal.py +129 -0
- contextsynapse/rules/loader.py +52 -0
- contextsynapse/rules/models.py +124 -0
- contextsynapse/rules/universal.py +393 -0
- contextsynapse/scheduler.py +229 -0
- contextsynapse/schema/__init__.py +42 -0
- contextsynapse/schema/compiler.py +252 -0
- contextsynapse/schema/composer.py +158 -0
- contextsynapse/schema/dedup.py +73 -0
- contextsynapse/schema/derivation.py +172 -0
- contextsynapse/schema/sdl.py +326 -0
- contextsynapse/schema/validation_gate.py +124 -0
- contextsynapse/sdk/__init__.py +63 -0
- contextsynapse/sdk/agent.py +214 -0
- contextsynapse/sdk/client.py +269 -0
- contextsynapse/sdk/exceptions.py +23 -0
- contextsynapse/sdk/models.py +78 -0
- contextsynapse/sdk/session.py +408 -0
- contextsynapse/sdk/tracked.py +218 -0
- contextsynapse/sdk/worker.py +266 -0
- contextsynapse/sdk/workspace_client.py +98 -0
- contextsynapse/sdk/ws.py +41 -0
- contextsynapse/search/__init__.py +20 -0
- contextsynapse/search/embedding_cache.py +140 -0
- contextsynapse/search/enhanced_search.py +152 -0
- contextsynapse/search/fulltext.py +130 -0
- contextsynapse/search/graph_search.py +634 -0
- contextsynapse/search/lmdb_index.py +1218 -0
- contextsynapse/search/nl_to_aiql.py +199 -0
- contextsynapse/search/rag.py +1346 -0
- contextsynapse/search/rag_cache.py +167 -0
- contextsynapse/search/reasoning_chain.py +231 -0
- contextsynapse/search/redis_search.py +207 -0
- contextsynapse/search/retrieval_quality.py +314 -0
- contextsynapse/search/semantic_query.py +455 -0
- contextsynapse/search/text_resolver.py +159 -0
- contextsynapse/search/whoosh_search.py +373 -0
- contextsynapse/security/__init__.py +17 -0
- contextsynapse/security/audit.py +297 -0
- contextsynapse/security/audit_logger.py +97 -0
- contextsynapse/security/audit_trail.py +270 -0
- contextsynapse/security/auth_unified.py +284 -0
- contextsynapse/security/auto_tagger.py +230 -0
- contextsynapse/security/data_security.py +218 -0
- contextsynapse/security/encryption.py +207 -0
- contextsynapse/security/identity.py +447 -0
- contextsynapse/security/jwt_identity.py +59 -0
- contextsynapse/security/middleware.py +336 -0
- contextsynapse/security/pii.py +233 -0
- contextsynapse/security/rbac.py +96 -0
- contextsynapse/security/rls.py +205 -0
- contextsynapse/security/sanitize.py +233 -0
- contextsynapse/security/scoped_encryption.py +131 -0
- contextsynapse/security/tenant.py +401 -0
- contextsynapse/shield/__init__.py +21 -0
- contextsynapse/shield/anomaly.py +56 -0
- contextsynapse/shield/permissions.py +99 -0
- contextsynapse/shield/profile.py +103 -0
- contextsynapse/shield/shield.py +108 -0
- contextsynapse/shield/trust_engine.py +69 -0
- contextsynapse/skills/__init__.py +14 -0
- contextsynapse/skills/engine.py +233 -0
- contextsynapse/skills/loader.py +59 -0
- contextsynapse/skills/models.py +139 -0
- contextsynapse/storage/__init__.py +7 -0
- contextsynapse/storage/cache.py +208 -0
- contextsynapse/storage/columnar_store_v2.py +178 -0
- contextsynapse/storage/csr_graph_storage.py +587 -0
- contextsynapse/storage/csr_store.py +388 -0
- contextsynapse/storage/document_store.py +491 -0
- contextsynapse/storage/hnsw_index.py +294 -0
- contextsynapse/storage/hybrid_store_v2.py +278 -0
- contextsynapse/storage/lmdb_graph_storage.py +366 -0
- contextsynapse/storage/migration.py +279 -0
- contextsynapse/storage/namespace_store.py +346 -0
- contextsynapse/storage/pure_graph_storage.py +185 -0
- contextsynapse/storage/redis_graph_adapter.py +186 -0
- contextsynapse/storage/redis_graph_storage.py +375 -0
- contextsynapse/storage/router/__init__.py +29 -0
- contextsynapse/storage/router/duckdb_store.py +398 -0
- contextsynapse/storage/router/factory.py +83 -0
- contextsynapse/storage/router/interface.py +125 -0
- contextsynapse/storage/router/postgres_store.py +339 -0
- contextsynapse/storage/router/resolver.py +192 -0
- contextsynapse/storage/router/router.py +300 -0
- contextsynapse/storage/single_file_storage.py +391 -0
- contextsynapse/storage/storage_manager.py +214 -0
- contextsynapse/storage/storage_query_optimizer.py +314 -0
- contextsynapse/storage/wal.py +359 -0
- contextsynapse/temporal/__init__.py +0 -0
- contextsynapse/temporal/storage.py +106 -0
- contextsynapse/tests/__init__.py +11 -0
- contextsynapse/tests/regression/__init__.py +1 -0
- contextsynapse/tests/regression/add_metadata.py +395 -0
- contextsynapse/tests/regression/add_skip_flags.py +85 -0
- contextsynapse/tests/regression/cases/test_basic_create_node.yaml +20 -0
- contextsynapse/tests/regression/cases/test_basic_select.yaml +21 -0
- contextsynapse/tests/regression/cases/test_blockchain_audit_trail.yaml +37 -0
- contextsynapse/tests/regression/cases/test_blockchain_audit_trail_filtered.yaml +39 -0
- contextsynapse/tests/regression/cases/test_blockchain_blocks_range.yaml +39 -0
- contextsynapse/tests/regression/cases/test_blockchain_get_block.yaml +35 -0
- contextsynapse/tests/regression/cases/test_blockchain_latest_block.yaml +35 -0
- contextsynapse/tests/regression/cases/test_blockchain_length.yaml +37 -0
- contextsynapse/tests/regression/cases/test_blockchain_merkle_root.yaml +37 -0
- contextsynapse/tests/regression/cases/test_blockchain_verify.yaml +35 -0
- contextsynapse/tests/regression/cases/test_blockchain_verify_block.yaml +35 -0
- contextsynapse/tests/regression/cases/test_create_edge.yaml +28 -0
- contextsynapse/tests/regression/cases/test_delete_edge_basic.yaml +29 -0
- contextsynapse/tests/regression/cases/test_delete_node_basic.yaml +29 -0
- contextsynapse/tests/regression/cases/test_evaluation_mrr.yaml +40 -0
- contextsynapse/tests/regression/cases/test_evaluation_ndcg.yaml +40 -0
- contextsynapse/tests/regression/cases/test_evaluation_precision.yaml +40 -0
- contextsynapse/tests/regression/cases/test_evaluation_recall.yaml +40 -0
- contextsynapse/tests/regression/cases/test_group_by_basic.yaml +56 -0
- contextsynapse/tests/regression/cases/test_match_node_basic.yaml +29 -0
- contextsynapse/tests/regression/cases/test_match_node_with_order_limit.yaml +31 -0
- contextsynapse/tests/regression/cases/test_namespace_switch.yaml +18 -0
- contextsynapse/tests/regression/cases/test_pipeline_cascaded.yaml +61 -0
- contextsynapse/tests/regression/cases/test_pipeline_fixed_size_chunking.yaml +48 -0
- contextsynapse/tests/regression/cases/test_pipeline_gpt4_chunking.yaml +54 -0
- contextsynapse/tests/regression/cases/test_pipeline_hybrid_indexing.yaml +57 -0
- contextsynapse/tests/regression/cases/test_pipeline_large_embedding.yaml +52 -0
- contextsynapse/tests/regression/cases/test_pipeline_paragraph_chunking.yaml +49 -0
- contextsynapse/tests/regression/cases/test_pipeline_recursive_chunking.yaml +47 -0
- contextsynapse/tests/regression/cases/test_pipeline_run_pipeline.yaml +56 -0
- contextsynapse/tests/regression/cases/test_pipeline_section_based_chunking.yaml +53 -0
- contextsynapse/tests/regression/cases/test_pipeline_semantic_chunking.yaml +51 -0
- contextsynapse/tests/regression/cases/test_pipeline_with_entity_extraction.yaml +57 -0
- contextsynapse/tests/regression/cases/test_pipeline_with_relationships.yaml +62 -0
- contextsynapse/tests/regression/cases/test_rag_evaluation.yaml +38 -0
- contextsynapse/tests/regression/cases/test_rag_graph_bfs_with_eval.yaml +48 -0
- contextsynapse/tests/regression/cases/test_rag_graph_centrality.yaml +40 -0
- contextsynapse/tests/regression/cases/test_rag_graph_centrality_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_community.yaml +40 -0
- contextsynapse/tests/regression/cases/test_rag_graph_community_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_dfs_with_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_multi_hop_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_path.yaml +40 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_basic.yaml +38 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_bfs.yaml +52 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_dfs.yaml +41 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_multi_hop.yaml +43 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_shortest_path.yaml +41 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_weighted.yaml +43 -0
- contextsynapse/tests/regression/cases/test_rag_graph_shortest_path_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_traversal.yaml +30 -0
- contextsynapse/tests/regression/cases/test_rag_graph_weighted_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_hybrid_search.yaml +33 -0
- contextsynapse/tests/regression/cases/test_rag_hybrid_with_prompt_embedding.yaml +44 -0
- contextsynapse/tests/regression/cases/test_rag_keyword_search.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_multi_hop_graph.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_path_based.yaml +34 -0
- contextsynapse/tests/regression/cases/test_rag_profile_based.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_vector_search.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_weighted_graph.yaml +34 -0
- contextsynapse/tests/regression/cases/test_rag_weighted_hybrid.yaml +31 -0
- contextsynapse/tests/regression/cases/test_semantic_boundary_chunking.yaml +57 -0
- contextsynapse/tests/regression/cases/test_semantic_boundary_detection.yaml +33 -0
- contextsynapse/tests/regression/cases/test_semantic_hash_function.yaml +30 -0
- contextsynapse/tests/regression/cases/test_semantic_hash_with_model.yaml +31 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_fixed_size.yaml +44 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_recursive.yaml +46 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_section.yaml +46 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_semantic.yaml +44 -0
- contextsynapse/tests/regression/cases/test_time_travel_as_of.yaml +35 -0
- contextsynapse/tests/regression/cases/test_time_travel_at_commit.yaml +33 -0
- contextsynapse/tests/regression/cases/test_time_travel_at_timestamp.yaml +33 -0
- contextsynapse/tests/regression/cases/test_time_travel_for_system_time_all.yaml +37 -0
- contextsynapse/tests/regression/cases/test_time_travel_versions_between.yaml +39 -0
- contextsynapse/tests/regression/cases/test_traverse_basic.yaml +31 -0
- contextsynapse/tests/regression/cases/test_traverse_max_depth.yaml +34 -0
- contextsynapse/tests/regression/cases/test_update_edge_basic.yaml +29 -0
- contextsynapse/tests/regression/evaluation_metrics.py +227 -0
- contextsynapse/tests/regression/regression_runner.py +1081 -0
- contextsynapse/tests/regression/setup_regression_data.py +212 -0
- contextsynapse/tests/regression/test_basic_only.py +84 -0
- contextsynapse/tests/regression/test_namespace_creation.py +29 -0
- contextsynapse/tests/regression/test_single_fix.py +61 -0
- contextsynapse/tests/regression/test_single_query.py +20 -0
- contextsynapse/tests/regression/validate_test_syntax.py +179 -0
- contextsynapse/tests/test_advanced_features.py +347 -0
- contextsynapse/tests/test_aiql_summary.py +283 -0
- contextsynapse/tests/test_all_aiql_queries.py +948 -0
- contextsynapse/tests/test_all_create_queries.py +213 -0
- contextsynapse/tests/test_api_vs_native.py +280 -0
- contextsynapse/tests/test_chunking_dedup.py +199 -0
- contextsynapse/tests/test_connectors.py +171 -0
- contextsynapse/tests/test_edges_and_match.py +202 -0
- contextsynapse/tests/test_extraction_pipeline_integration.py +581 -0
- contextsynapse/tests/test_name_property.py +224 -0
- contextsynapse/tests/test_node_edge_metadata.py +250 -0
- contextsynapse/tests/test_playground_conn.py +114 -0
- contextsynapse/tests/test_query_cache.py +213 -0
- contextsynapse/tests/test_source_policy_auth.py +348 -0
- contextsynapse/tests/test_temporal_and_domain.py +224 -0
- contextsynapse/tests/test_time_travel_e2e.py +571 -0
- contextsynapse/tests/test_traversal_and_aggregation.py +275 -0
- contextsynapse/tools/__init__.py +36 -0
- contextsynapse/tools/a2a_tools.py +106 -0
- contextsynapse/tools/cognition_tools.py +118 -0
- contextsynapse/tools/context.py +905 -0
- contextsynapse/tools/formatting.py +92 -0
- contextsynapse/tools/gateway_tools.py +186 -0
- contextsynapse/tools/graph.py +1700 -0
- contextsynapse/tools/intelligence.py +235 -0
- contextsynapse/tools/memory.py +118 -0
- contextsynapse/tools/registry.py +373 -0
- contextsynapse/tools/shield_tools.py +159 -0
- contextsynapse/tools/tasks.py +341 -0
- contextsynapse/tools/workspace_tools.py +124 -0
- contextsynapse/utils/__init__.py +15 -0
- contextsynapse/utils/config_loader.py +198 -0
- contextsynapse/utils/env_loader.py +156 -0
- contextsynapse/utils/file_utils.py +288 -0
- contextsynapse/utils/logging_optimizer.py +119 -0
- contextsynapse/vector/__init__.py +30 -0
- contextsynapse/vector/chroma_vector_store.py +152 -0
- contextsynapse/vector/custom_vector_store.py +537 -0
- contextsynapse/vector/faiss_vector_store.py +296 -0
- contextsynapse/vector/qdrant_vector_store.py +163 -0
- contextsynapse/vector/vector_db.py +953 -0
- contextsynapse/vector/vector_db_manager.py +197 -0
- contextsynapse/workspace/__init__.py +27 -0
- contextsynapse/workspace/base.py +94 -0
- contextsynapse/workspace/git.py +228 -0
- contextsynapse/workspace/github.py +78 -0
- contextsynapse/workspace/local.py +73 -0
- contextsynapse/workspace/tools.py +222 -0
- contextsynapse-1.0.0.dist-info/METADATA +847 -0
- contextsynapse-1.0.0.dist-info/RECORD +776 -0
- contextsynapse-1.0.0.dist-info/WHEEL +5 -0
- contextsynapse-1.0.0.dist-info/entry_points.txt +4 -0
- contextsynapse-1.0.0.dist-info/licenses/LICENSE +200 -0
- contextsynapse-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
"""TenantIsolation — ensures data separation between AMCs/organizations.
|
|
2
|
+
|
|
3
|
+
Every resource belongs to a tenant. Users can only access resources
|
|
4
|
+
within their tenant. Cross-tenant access is never allowed (even for admins).
|
|
5
|
+
|
|
6
|
+
In dev mode: a single default tenant is used.
|
|
7
|
+
In production: tenant is extracted from JWT and enforced on every query.
|
|
8
|
+
|
|
9
|
+
Namespace isolation (legacy):
|
|
10
|
+
ti = TenantIsolation()
|
|
11
|
+
ns = ti.namespace("alpha_capital", "portfolio_growth")
|
|
12
|
+
# -> "alpha_capital_portfolio_growth"
|
|
13
|
+
|
|
14
|
+
ti.validate_access("alpha_capital", ns) # True
|
|
15
|
+
ti.validate_access("beta_wealth", ns) # False
|
|
16
|
+
ti.validate_access("anyone", "market_nifty50") # True (shared)
|
|
17
|
+
|
|
18
|
+
Tenant management (new — PostgreSQL-backed):
|
|
19
|
+
tm = TenantManager()
|
|
20
|
+
tenant = tm.create_tenant("Alpha Capital", "alpha-capital", plan="pro")
|
|
21
|
+
tm.get_tenant(tenant["id"])
|
|
22
|
+
tm.suspend_tenant(tenant["id"], reason="Non-payment")
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import logging
|
|
27
|
+
import os
|
|
28
|
+
import re
|
|
29
|
+
import uuid
|
|
30
|
+
from datetime import datetime, timezone
|
|
31
|
+
from typing import Any, Dict, List, Optional, Set
|
|
32
|
+
|
|
33
|
+
from fastapi import HTTPException, Request
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# ── Shared namespace prefixes (platform-level data) ──────────────────────────
|
|
39
|
+
# Namespace prefixes that are shared across all tenants
|
|
40
|
+
SHARED_PREFIXES: List[str] = [
|
|
41
|
+
"market_",
|
|
42
|
+
"stock_",
|
|
43
|
+
"macro_",
|
|
44
|
+
"regulatory_",
|
|
45
|
+
"fund_nav_",
|
|
46
|
+
"sector_",
|
|
47
|
+
"index_",
|
|
48
|
+
"currency_",
|
|
49
|
+
"fii_",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
# Known tenant IDs (populated at runtime)
|
|
53
|
+
_known_tenants: Set[str] = set()
|
|
54
|
+
|
|
55
|
+
# Default tenant for dev mode
|
|
56
|
+
DEFAULT_TENANT_SLUG = "dev"
|
|
57
|
+
DEFAULT_TENANT_NAME = "Development"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# ── Namespace isolation (legacy, kept for backward compat) ───────────────────
|
|
61
|
+
|
|
62
|
+
class TenantIsolation:
|
|
63
|
+
"""Enforces tenant-scoped namespace isolation."""
|
|
64
|
+
|
|
65
|
+
def __init__(self):
|
|
66
|
+
self._known_tenants: Set[str] = set()
|
|
67
|
+
|
|
68
|
+
def namespace(self, tenant_id: str, resource: str) -> str:
|
|
69
|
+
"""Create a tenant-scoped namespace."""
|
|
70
|
+
safe_tenant = re.sub(r'[^a-zA-Z0-9_]', '_', tenant_id)
|
|
71
|
+
safe_resource = re.sub(r'[^a-zA-Z0-9_]', '_', resource)
|
|
72
|
+
self._known_tenants.add(safe_tenant)
|
|
73
|
+
return f"{safe_tenant}_{safe_resource}"
|
|
74
|
+
|
|
75
|
+
def is_shared(self, namespace: str) -> bool:
|
|
76
|
+
"""Check if a namespace is shared (platform-level market data)."""
|
|
77
|
+
return any(namespace.startswith(prefix) for prefix in SHARED_PREFIXES)
|
|
78
|
+
|
|
79
|
+
def extract_tenant(self, namespace: str) -> Optional[str]:
|
|
80
|
+
"""Extract tenant ID from a namespaced string."""
|
|
81
|
+
if self.is_shared(namespace):
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
# Try to match against known tenants
|
|
85
|
+
for tenant in self._known_tenants:
|
|
86
|
+
if namespace.startswith(f"{tenant}_"):
|
|
87
|
+
return tenant
|
|
88
|
+
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
def validate_access(
|
|
92
|
+
self, tenant_id: str, namespace: str
|
|
93
|
+
) -> bool:
|
|
94
|
+
"""Check if a tenant can access a namespace."""
|
|
95
|
+
# Shared namespaces accessible to all
|
|
96
|
+
if self.is_shared(namespace):
|
|
97
|
+
return True
|
|
98
|
+
|
|
99
|
+
# Tenant can access their own namespaces
|
|
100
|
+
safe_tenant = re.sub(r'[^a-zA-Z0-9_]', '_', tenant_id)
|
|
101
|
+
return namespace.startswith(f"{safe_tenant}_")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# ── Tenant Manager (PostgreSQL-backed) ───────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
class TenantManager:
|
|
107
|
+
"""Manages tenants in the database (PostgreSQL or SQLite).
|
|
108
|
+
|
|
109
|
+
Uses contextsynapse.core.db for database access, so it works with both
|
|
110
|
+
backends seamlessly.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
_DDL_PG = """
|
|
114
|
+
CREATE TABLE IF NOT EXISTS tenants (
|
|
115
|
+
id TEXT PRIMARY KEY,
|
|
116
|
+
name TEXT NOT NULL,
|
|
117
|
+
slug TEXT NOT NULL UNIQUE,
|
|
118
|
+
plan TEXT NOT NULL DEFAULT 'free',
|
|
119
|
+
settings TEXT NOT NULL DEFAULT '{}',
|
|
120
|
+
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
121
|
+
suspended BOOLEAN NOT NULL DEFAULT FALSE,
|
|
122
|
+
suspend_reason TEXT,
|
|
123
|
+
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
|
124
|
+
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
|
|
125
|
+
);
|
|
126
|
+
CREATE INDEX IF NOT EXISTS idx_tenants_slug ON tenants(slug);
|
|
127
|
+
CREATE INDEX IF NOT EXISTS idx_tenants_active ON tenants(is_active);
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
_DDL_SQLITE = """
|
|
131
|
+
CREATE TABLE IF NOT EXISTS tenants (
|
|
132
|
+
id TEXT PRIMARY KEY,
|
|
133
|
+
name TEXT NOT NULL,
|
|
134
|
+
slug TEXT NOT NULL UNIQUE,
|
|
135
|
+
plan TEXT NOT NULL DEFAULT 'free',
|
|
136
|
+
settings TEXT NOT NULL DEFAULT '{}',
|
|
137
|
+
is_active INTEGER NOT NULL DEFAULT 1,
|
|
138
|
+
suspended INTEGER NOT NULL DEFAULT 0,
|
|
139
|
+
suspend_reason TEXT,
|
|
140
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
141
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
142
|
+
);
|
|
143
|
+
CREATE INDEX IF NOT EXISTS idx_tenants_slug ON tenants(slug);
|
|
144
|
+
CREATE INDEX IF NOT EXISTS idx_tenants_active ON tenants(is_active);
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
def __init__(self):
|
|
148
|
+
from ..core.db import IS_POSTGRES
|
|
149
|
+
self._is_pg = IS_POSTGRES
|
|
150
|
+
self._ensure_table()
|
|
151
|
+
|
|
152
|
+
def _ensure_table(self):
|
|
153
|
+
"""Create tenants table if it doesn't exist."""
|
|
154
|
+
from ..core.db import transaction, run_ddl, IS_POSTGRES
|
|
155
|
+
try:
|
|
156
|
+
with transaction() as (conn, _cur):
|
|
157
|
+
ddl = self._DDL_PG if IS_POSTGRES else self._DDL_SQLITE
|
|
158
|
+
run_ddl(conn, ddl)
|
|
159
|
+
except Exception as exc:
|
|
160
|
+
logger.warning("Could not create tenants table: %s", exc)
|
|
161
|
+
|
|
162
|
+
# ── CRUD ─────────────────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
def create_tenant(
|
|
165
|
+
self,
|
|
166
|
+
name: str,
|
|
167
|
+
slug: str,
|
|
168
|
+
plan: str = "free",
|
|
169
|
+
settings: Optional[Dict[str, Any]] = None,
|
|
170
|
+
) -> Dict[str, Any]:
|
|
171
|
+
"""Create a new tenant. Returns the tenant dict."""
|
|
172
|
+
import json
|
|
173
|
+
from ..core.db import transaction, PH, integrity_error
|
|
174
|
+
|
|
175
|
+
tenant_id = str(uuid.uuid4())
|
|
176
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
177
|
+
settings_json = json.dumps(settings or {})
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
with transaction() as (conn, cur):
|
|
181
|
+
cur.execute(
|
|
182
|
+
f"INSERT INTO tenants (id, name, slug, plan, settings, created_at, updated_at) "
|
|
183
|
+
f"VALUES ({PH}, {PH}, {PH}, {PH}, {PH}, {PH}, {PH})",
|
|
184
|
+
(tenant_id, name, slug, plan, settings_json, now, now),
|
|
185
|
+
)
|
|
186
|
+
except integrity_error():
|
|
187
|
+
raise ValueError(f"Tenant with slug '{slug}' already exists")
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
"id": tenant_id,
|
|
191
|
+
"name": name,
|
|
192
|
+
"slug": slug,
|
|
193
|
+
"plan": plan,
|
|
194
|
+
"settings": settings or {},
|
|
195
|
+
"is_active": True,
|
|
196
|
+
"suspended": False,
|
|
197
|
+
"suspend_reason": None,
|
|
198
|
+
"created_at": now,
|
|
199
|
+
"updated_at": now,
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
def get_tenant(self, tenant_id: str) -> Optional[Dict[str, Any]]:
|
|
203
|
+
"""Get a tenant by ID."""
|
|
204
|
+
from ..core.db import transaction, PH, row_to_dict
|
|
205
|
+
with transaction() as (_conn, cur):
|
|
206
|
+
cur.execute(
|
|
207
|
+
f"SELECT * FROM tenants WHERE id = {PH}", (tenant_id,)
|
|
208
|
+
)
|
|
209
|
+
row = cur.fetchone()
|
|
210
|
+
return self._parse_row(row_to_dict(row))
|
|
211
|
+
|
|
212
|
+
def get_tenant_by_slug(self, slug: str) -> Optional[Dict[str, Any]]:
|
|
213
|
+
"""Get a tenant by slug."""
|
|
214
|
+
from ..core.db import transaction, PH, row_to_dict
|
|
215
|
+
with transaction() as (_conn, cur):
|
|
216
|
+
cur.execute(
|
|
217
|
+
f"SELECT * FROM tenants WHERE slug = {PH}", (slug,)
|
|
218
|
+
)
|
|
219
|
+
row = cur.fetchone()
|
|
220
|
+
return self._parse_row(row_to_dict(row))
|
|
221
|
+
|
|
222
|
+
def list_tenants(self) -> List[Dict[str, Any]]:
|
|
223
|
+
"""List all tenants."""
|
|
224
|
+
from ..core.db import transaction, row_to_dict
|
|
225
|
+
with transaction() as (_conn, cur):
|
|
226
|
+
cur.execute("SELECT * FROM tenants ORDER BY created_at")
|
|
227
|
+
rows = cur.fetchall()
|
|
228
|
+
return [
|
|
229
|
+
self._parse_row(row_to_dict(r))
|
|
230
|
+
for r in rows
|
|
231
|
+
if row_to_dict(r) is not None
|
|
232
|
+
]
|
|
233
|
+
|
|
234
|
+
def update_tenant(self, tenant_id: str, **updates) -> Optional[Dict[str, Any]]:
|
|
235
|
+
"""Update tenant fields. Allowed: name, plan, settings, is_active."""
|
|
236
|
+
import json
|
|
237
|
+
from ..core.db import transaction, PH
|
|
238
|
+
|
|
239
|
+
allowed = {"name", "plan", "settings", "is_active"}
|
|
240
|
+
filtered = {k: v for k, v in updates.items() if k in allowed}
|
|
241
|
+
if not filtered:
|
|
242
|
+
return self.get_tenant(tenant_id)
|
|
243
|
+
|
|
244
|
+
# Serialize settings to JSON if present
|
|
245
|
+
if "settings" in filtered and isinstance(filtered["settings"], dict):
|
|
246
|
+
filtered["settings"] = json.dumps(filtered["settings"])
|
|
247
|
+
|
|
248
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
249
|
+
filtered["updated_at"] = now
|
|
250
|
+
|
|
251
|
+
set_clauses = ", ".join(f"{k} = {PH}" for k in filtered)
|
|
252
|
+
values = list(filtered.values()) + [tenant_id]
|
|
253
|
+
|
|
254
|
+
with transaction() as (_conn, cur):
|
|
255
|
+
cur.execute(
|
|
256
|
+
f"UPDATE tenants SET {set_clauses} WHERE id = {PH}",
|
|
257
|
+
tuple(values),
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
return self.get_tenant(tenant_id)
|
|
261
|
+
|
|
262
|
+
def suspend_tenant(self, tenant_id: str, reason: str = "") -> Optional[Dict[str, Any]]:
|
|
263
|
+
"""Suspend a tenant (block all access)."""
|
|
264
|
+
from ..core.db import transaction, PH
|
|
265
|
+
|
|
266
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
267
|
+
with transaction() as (_conn, cur):
|
|
268
|
+
cur.execute(
|
|
269
|
+
f"UPDATE tenants SET suspended = {PH}, suspend_reason = {PH}, "
|
|
270
|
+
f"updated_at = {PH} WHERE id = {PH}",
|
|
271
|
+
(True if self._is_pg else 1, reason, now, tenant_id),
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
logger.warning("Tenant %s suspended: %s", tenant_id, reason)
|
|
275
|
+
return self.get_tenant(tenant_id)
|
|
276
|
+
|
|
277
|
+
def get_or_create_default(self) -> Dict[str, Any]:
|
|
278
|
+
"""Get or create the default 'dev' tenant (for development mode)."""
|
|
279
|
+
existing = self.get_tenant_by_slug(DEFAULT_TENANT_SLUG)
|
|
280
|
+
if existing:
|
|
281
|
+
return existing
|
|
282
|
+
|
|
283
|
+
return self.create_tenant(
|
|
284
|
+
name=DEFAULT_TENANT_NAME,
|
|
285
|
+
slug=DEFAULT_TENANT_SLUG,
|
|
286
|
+
plan="free",
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
# ── Middleware helper ────────────────────────────────────────────────────
|
|
290
|
+
|
|
291
|
+
def ensure_tenant_context(self, tenant_id: str) -> Dict[str, Any]:
|
|
292
|
+
"""Validate that the tenant exists and is active. Returns tenant dict.
|
|
293
|
+
|
|
294
|
+
Raises HTTPException if tenant is not found or suspended.
|
|
295
|
+
Called by middleware to set the current tenant on the request.
|
|
296
|
+
"""
|
|
297
|
+
tenant = self.get_tenant(tenant_id)
|
|
298
|
+
if not tenant:
|
|
299
|
+
raise HTTPException(status_code=404, detail="Tenant not found")
|
|
300
|
+
if tenant.get("suspended"):
|
|
301
|
+
raise HTTPException(
|
|
302
|
+
status_code=403,
|
|
303
|
+
detail=f"Tenant suspended: {tenant.get('suspend_reason', 'Contact support')}",
|
|
304
|
+
)
|
|
305
|
+
if not tenant.get("is_active"):
|
|
306
|
+
raise HTTPException(status_code=403, detail="Tenant is inactive")
|
|
307
|
+
return tenant
|
|
308
|
+
|
|
309
|
+
# ── Internal helpers ─────────────────────────────────────────────────────
|
|
310
|
+
|
|
311
|
+
@staticmethod
|
|
312
|
+
def _parse_row(row: Optional[Dict]) -> Optional[Dict[str, Any]]:
|
|
313
|
+
"""Parse a raw DB row into a clean tenant dict."""
|
|
314
|
+
if row is None:
|
|
315
|
+
return None
|
|
316
|
+
import json
|
|
317
|
+
result = dict(row)
|
|
318
|
+
# Parse settings JSON
|
|
319
|
+
if isinstance(result.get("settings"), str):
|
|
320
|
+
try:
|
|
321
|
+
result["settings"] = json.loads(result["settings"])
|
|
322
|
+
except (json.JSONDecodeError, TypeError):
|
|
323
|
+
result["settings"] = {}
|
|
324
|
+
# Normalize booleans (SQLite stores as 0/1)
|
|
325
|
+
for key in ("is_active", "suspended"):
|
|
326
|
+
if key in result:
|
|
327
|
+
result[key] = bool(result[key])
|
|
328
|
+
return result
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
# ── Tenant middleware ────────────────────────────────────────────────────────
|
|
332
|
+
|
|
333
|
+
_tenant_manager: Optional[TenantManager] = None
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _get_tenant_manager() -> TenantManager:
|
|
337
|
+
"""Lazy singleton for TenantManager."""
|
|
338
|
+
global _tenant_manager
|
|
339
|
+
if _tenant_manager is None:
|
|
340
|
+
_tenant_manager = TenantManager()
|
|
341
|
+
return _tenant_manager
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _is_production() -> bool:
|
|
345
|
+
return os.environ.get("CONTEXTSYNAPSE_ENV") or os.environ.get("AICONTEXTDB_ENV", "").lower() == "production"
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
async def tenant_middleware(request: Request, call_next):
|
|
349
|
+
"""Extract tenant from JWT and set on request state.
|
|
350
|
+
|
|
351
|
+
In dev mode: use default tenant.
|
|
352
|
+
In production: extract from JWT, validate tenant is active.
|
|
353
|
+
"""
|
|
354
|
+
# Skip tenant check for health/docs endpoints
|
|
355
|
+
path = request.url.path
|
|
356
|
+
if path in ("/health", "/docs", "/openapi.json", "/redoc"):
|
|
357
|
+
response = await call_next(request)
|
|
358
|
+
return response
|
|
359
|
+
|
|
360
|
+
tm = _get_tenant_manager()
|
|
361
|
+
|
|
362
|
+
if not _is_production():
|
|
363
|
+
# Dev mode: use default tenant
|
|
364
|
+
default = tm.get_or_create_default()
|
|
365
|
+
request.state.tenant_id = default["id"]
|
|
366
|
+
request.state.tenant = default
|
|
367
|
+
response = await call_next(request)
|
|
368
|
+
return response
|
|
369
|
+
|
|
370
|
+
# Production: extract tenant from JWT claims (set by auth middleware)
|
|
371
|
+
tenant_id = None
|
|
372
|
+
|
|
373
|
+
# Check if auth middleware already set the tenant
|
|
374
|
+
if hasattr(request.state, "tenant_id") and request.state.tenant_id:
|
|
375
|
+
tenant_id = request.state.tenant_id
|
|
376
|
+
else:
|
|
377
|
+
# Try extracting from Authorization header JWT
|
|
378
|
+
auth_header = request.headers.get("Authorization", "")
|
|
379
|
+
if auth_header.startswith("Bearer "):
|
|
380
|
+
token = auth_header[7:]
|
|
381
|
+
try:
|
|
382
|
+
from ..api.auth import verify_jwt
|
|
383
|
+
payload = verify_jwt(token)
|
|
384
|
+
if payload:
|
|
385
|
+
tenant_id = payload.get("tenant") or payload.get("tenant_id")
|
|
386
|
+
except Exception:
|
|
387
|
+
pass
|
|
388
|
+
|
|
389
|
+
if not tenant_id:
|
|
390
|
+
raise HTTPException(
|
|
391
|
+
status_code=401,
|
|
392
|
+
detail="Tenant context required. Include tenant in JWT.",
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
# Validate tenant is active and not suspended
|
|
396
|
+
tenant = tm.ensure_tenant_context(tenant_id)
|
|
397
|
+
request.state.tenant_id = tenant_id
|
|
398
|
+
request.state.tenant = tenant
|
|
399
|
+
|
|
400
|
+
response = await call_next(request)
|
|
401
|
+
return response
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""AgentShield — continuous behavioral auth + adaptive trust."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
_shield = None
|
|
5
|
+
|
|
6
|
+
def get_agent_shield():
|
|
7
|
+
global _shield
|
|
8
|
+
if _shield is None:
|
|
9
|
+
redis_client = None
|
|
10
|
+
try:
|
|
11
|
+
import os, redis
|
|
12
|
+
url = os.environ.get("CONTEXTSYNAPSE_REDIS_URL") or os.environ.get("AICONTEXTDB_REDIS_URL")
|
|
13
|
+
if url:
|
|
14
|
+
redis_client = redis.from_url(url)
|
|
15
|
+
except Exception:
|
|
16
|
+
pass
|
|
17
|
+
from .shield import AgentShield
|
|
18
|
+
_shield = AgentShield(redis_client=redis_client)
|
|
19
|
+
return _shield
|
|
20
|
+
|
|
21
|
+
__all__ = ["get_agent_shield"]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""AnomalyDetector — statistical anomaly detection for agent behavior."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import json, math
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Any, Dict, List
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class AnomalyResult:
|
|
9
|
+
score: float = 0.0
|
|
10
|
+
flags: List[str] = field(default_factory=list)
|
|
11
|
+
details: Dict[str, Any] = field(default_factory=dict)
|
|
12
|
+
frozen: bool = False
|
|
13
|
+
|
|
14
|
+
class AnomalyDetector:
|
|
15
|
+
MIN_TOOL_CALLS = 20
|
|
16
|
+
MIN_SESSIONS = 2
|
|
17
|
+
|
|
18
|
+
def analyze(self, profile: Dict, session: Dict) -> AnomalyResult:
|
|
19
|
+
if profile.get("total_tool_calls", 0) < self.MIN_TOOL_CALLS:
|
|
20
|
+
return AnomalyResult()
|
|
21
|
+
if profile.get("sessions_count", 0) < self.MIN_SESSIONS:
|
|
22
|
+
return AnomalyResult()
|
|
23
|
+
flags = []
|
|
24
|
+
details = {}
|
|
25
|
+
hist_dist = json.loads(profile.get("tool_distribution", "{}"))
|
|
26
|
+
sess_dist = json.loads(session.get("tool_distribution", "{}"))
|
|
27
|
+
if hist_dist and sess_dist:
|
|
28
|
+
divergence = self._cosine_distance(hist_dist, sess_dist)
|
|
29
|
+
if divergence > 0.6:
|
|
30
|
+
flags.append("tool_pattern_change")
|
|
31
|
+
details["tool_divergence"] = round(divergence, 3)
|
|
32
|
+
hist_quality = profile.get("feedback_quality", 0.5)
|
|
33
|
+
sess_quality = session.get("feedback_quality")
|
|
34
|
+
if sess_quality is not None and profile.get("total_feedback_given", 0) > 10:
|
|
35
|
+
if sess_quality < hist_quality - 0.3:
|
|
36
|
+
flags.append("quality_drop")
|
|
37
|
+
details["quality_delta"] = round(sess_quality - hist_quality, 3)
|
|
38
|
+
hist_ratio = profile.get("read_write_ratio", 0.5)
|
|
39
|
+
sess_ratio = session.get("read_write_ratio")
|
|
40
|
+
if sess_ratio is not None:
|
|
41
|
+
if abs(sess_ratio - hist_ratio) > 0.4:
|
|
42
|
+
flags.append("unusual_read_scope")
|
|
43
|
+
details["ratio_delta"] = round(sess_ratio - hist_ratio, 3)
|
|
44
|
+
weights = {"tool_pattern_change": 0.35, "quality_drop": 0.30, "unusual_read_scope": 0.20}
|
|
45
|
+
score = min(sum(weights.get(f, 0.15) for f in flags), 1.0)
|
|
46
|
+
return AnomalyResult(score=round(score, 3), flags=flags, details=details, frozen=score >= 0.7)
|
|
47
|
+
|
|
48
|
+
@staticmethod
|
|
49
|
+
def _cosine_distance(a: Dict[str, int], b: Dict[str, int]) -> float:
|
|
50
|
+
all_keys = set(a) | set(b)
|
|
51
|
+
if not all_keys:
|
|
52
|
+
return 0.0
|
|
53
|
+
dot = sum(a.get(k, 0) * b.get(k, 0) for k in all_keys)
|
|
54
|
+
mag_a = math.sqrt(sum(v ** 2 for v in a.values())) or 1.0
|
|
55
|
+
mag_b = math.sqrt(sum(v ** 2 for v in b.values())) or 1.0
|
|
56
|
+
return round(1.0 - dot / (mag_a * mag_b), 4)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""AdaptivePermissions — map trust levels to tool access tiers."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Dict, Set
|
|
5
|
+
|
|
6
|
+
TIER_READ = {
|
|
7
|
+
"search_nodes", "get_context", "orient", "briefing", "ask", "recall",
|
|
8
|
+
"graph_summary", "query_graph", "rag_query", "rag_graph", "graph_timeline",
|
|
9
|
+
"graph_diff", "check_freshness", "get_suggestions", "project_status",
|
|
10
|
+
"list_tasks", "my_tasks", "get_agent_context", "get_unblocked_tasks",
|
|
11
|
+
"task_context", "ws_read_file", "ws_list_files", "ws_git_status",
|
|
12
|
+
"a2a_discover", "a2a_get_task", "my_usage", "boundary_costs", "get_tool_policy",
|
|
13
|
+
"explain_context", "topic_scan", "check_reviews", "trace_lineage",
|
|
14
|
+
"search", "code_project_status", "list_pipelines", "list_schemas",
|
|
15
|
+
"get_versions", "get_context_meta",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
TIER_WRITE = TIER_READ | {
|
|
19
|
+
"add_knowledge", "add_relationship", "add_decision", "add_task",
|
|
20
|
+
"claim_task", "complete_task", "handoff_task", "log_action",
|
|
21
|
+
"remember", "rate_context", "detect_conflicts", "resolve_conflict",
|
|
22
|
+
"report_external", "report_tokens", "a2a_send_task", "a2a_cancel_task",
|
|
23
|
+
"invalidate_node", "request_review", "request_promotion",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
TIER_FULL = TIER_WRITE | {
|
|
27
|
+
"dispatch_goal", "generate_tasks",
|
|
28
|
+
"ws_write_file", "ws_commit", "ws_push", "ws_create_branch", "ws_run_command",
|
|
29
|
+
"use_graph", "save_schema", "run_pipeline",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class PermissionDecision:
|
|
34
|
+
allowed: bool
|
|
35
|
+
reason: str = ""
|
|
36
|
+
|
|
37
|
+
class AdaptivePermissions:
|
|
38
|
+
def __init__(self):
|
|
39
|
+
self._frozen: Set[str] = set()
|
|
40
|
+
self._active: Dict[str, str] = {} # agent_id -> task_id (mid-workflow)
|
|
41
|
+
self._pending_restrictions: Dict[str, str] = {} # agent_id -> reason (apply after task)
|
|
42
|
+
|
|
43
|
+
def check(self, agent_id: str, tool_name: str, trust_score: float = 0.5) -> PermissionDecision:
|
|
44
|
+
# Grace period: if agent is mid-task, allow but warn
|
|
45
|
+
if agent_id in self._active:
|
|
46
|
+
is_restricted = (agent_id in self._frozen) or (trust_score < 0.2 and tool_name not in TIER_READ)
|
|
47
|
+
if is_restricted:
|
|
48
|
+
reason = "frozen" if agent_id in self._frozen else f"low trust ({trust_score:.2f})"
|
|
49
|
+
self._pending_restrictions[agent_id] = reason
|
|
50
|
+
return PermissionDecision(True, f"WARNING: {reason} — restrictions apply after current task completes")
|
|
51
|
+
# Normal permission check for active agents
|
|
52
|
+
return self._check_tier(agent_id, tool_name, trust_score)
|
|
53
|
+
|
|
54
|
+
# Not active — enforce restrictions immediately
|
|
55
|
+
# But first check if there's a pending restriction from a completed task
|
|
56
|
+
if agent_id in self._pending_restrictions:
|
|
57
|
+
if tool_name == "claim_task":
|
|
58
|
+
# Block new task claims — restrictions are now active
|
|
59
|
+
reason = self._pending_restrictions.pop(agent_id)
|
|
60
|
+
return PermissionDecision(False, f"Restricted: {reason}")
|
|
61
|
+
|
|
62
|
+
return self._check_tier(agent_id, tool_name, trust_score)
|
|
63
|
+
|
|
64
|
+
def _check_tier(self, agent_id: str, tool_name: str, trust_score: float) -> PermissionDecision:
|
|
65
|
+
if agent_id in self._frozen:
|
|
66
|
+
if tool_name in TIER_READ:
|
|
67
|
+
return PermissionDecision(True, "read-only (frozen)")
|
|
68
|
+
return PermissionDecision(False, "Agent frozen — anomaly detected")
|
|
69
|
+
if trust_score >= 0.8:
|
|
70
|
+
return PermissionDecision(True, "trusted")
|
|
71
|
+
if trust_score >= 0.5:
|
|
72
|
+
if tool_name in TIER_FULL:
|
|
73
|
+
return PermissionDecision(True, "verified")
|
|
74
|
+
return PermissionDecision(False, f"Requires trust >= 0.8 (current: {trust_score:.2f})")
|
|
75
|
+
if trust_score >= 0.2:
|
|
76
|
+
if tool_name in TIER_WRITE:
|
|
77
|
+
return PermissionDecision(True, "provisional")
|
|
78
|
+
return PermissionDecision(False, f"Requires trust >= 0.5 (current: {trust_score:.2f})")
|
|
79
|
+
if tool_name in TIER_READ:
|
|
80
|
+
return PermissionDecision(True, "untrusted: read-only")
|
|
81
|
+
return PermissionDecision(False, "Untrusted agent: read-only access")
|
|
82
|
+
|
|
83
|
+
def mark_active(self, agent_id: str, task_id: str):
|
|
84
|
+
"""Mark agent as mid-workflow (won't be blocked until task completes)."""
|
|
85
|
+
self._active[agent_id] = task_id
|
|
86
|
+
|
|
87
|
+
def mark_idle(self, agent_id: str):
|
|
88
|
+
"""Mark agent as idle (restrictions now enforced)."""
|
|
89
|
+
self._active.pop(agent_id, None)
|
|
90
|
+
|
|
91
|
+
def freeze(self, agent_id: str):
|
|
92
|
+
self._frozen.add(agent_id)
|
|
93
|
+
|
|
94
|
+
def unfreeze(self, agent_id: str):
|
|
95
|
+
self._frozen.discard(agent_id)
|
|
96
|
+
self._pending_restrictions.pop(agent_id, None)
|
|
97
|
+
|
|
98
|
+
def is_frozen(self, agent_id: str) -> bool:
|
|
99
|
+
return agent_id in self._frozen
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""AgentProfile — behavioral fingerprint per agent."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import json, logging
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
_DEFAULT_PROFILE = {
|
|
10
|
+
"total_reads": 0, "total_writes": 0, "total_tool_calls": 0,
|
|
11
|
+
"total_feedback_given": 0, "total_derivations": 0,
|
|
12
|
+
"feedback_useful": 0, "feedback_misleading": 0, "feedback_incorrect": 0, "feedback_outdated": 0,
|
|
13
|
+
"invalidations_caused": 0, "read_write_ratio": 0.5, "feedback_quality": 0.5,
|
|
14
|
+
"tool_distribution": "{}", "hour_distribution": "{}",
|
|
15
|
+
"sessions_count": 0, "consecutive_clean": 0,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class AgentProfile:
|
|
19
|
+
def __init__(self, redis_client=None):
|
|
20
|
+
self._redis = redis_client
|
|
21
|
+
self._memory: Dict[str, Dict[str, Any]] = {}
|
|
22
|
+
self._prefix = "contextcore:shield:profile"
|
|
23
|
+
|
|
24
|
+
def get(self, agent_id: str) -> Dict[str, Any]:
|
|
25
|
+
if self._redis:
|
|
26
|
+
try:
|
|
27
|
+
key = f"{self._prefix}:{agent_id}"
|
|
28
|
+
raw = self._redis.hgetall(key)
|
|
29
|
+
if raw:
|
|
30
|
+
data = dict(_DEFAULT_PROFILE)
|
|
31
|
+
for k, v in raw.items():
|
|
32
|
+
k = k.decode() if isinstance(k, bytes) else k
|
|
33
|
+
v = v.decode() if isinstance(v, bytes) else v
|
|
34
|
+
if k in data and isinstance(data[k], (int, float)):
|
|
35
|
+
data[k] = type(data[k])(v)
|
|
36
|
+
else:
|
|
37
|
+
data[k] = v
|
|
38
|
+
return data
|
|
39
|
+
except Exception:
|
|
40
|
+
pass
|
|
41
|
+
return dict(self._memory.get(agent_id, _DEFAULT_PROFILE))
|
|
42
|
+
|
|
43
|
+
def record_event(self, agent_id: str, event_type: str):
|
|
44
|
+
d = self._ensure(agent_id)
|
|
45
|
+
if event_type == "read":
|
|
46
|
+
d["total_reads"] += 1
|
|
47
|
+
elif event_type in ("write", "derive"):
|
|
48
|
+
d["total_writes"] += 1
|
|
49
|
+
if event_type == "derive":
|
|
50
|
+
d["total_derivations"] += 1
|
|
51
|
+
total = d["total_reads"] + d["total_writes"]
|
|
52
|
+
d["read_write_ratio"] = round(d["total_reads"] / total, 3) if total > 0 else 0.5
|
|
53
|
+
self._save(agent_id, d)
|
|
54
|
+
|
|
55
|
+
def record_tool_call(self, agent_id: str, tool_name: str):
|
|
56
|
+
d = self._ensure(agent_id)
|
|
57
|
+
d["total_tool_calls"] += 1
|
|
58
|
+
dist = json.loads(d["tool_distribution"])
|
|
59
|
+
dist[tool_name] = dist.get(tool_name, 0) + 1
|
|
60
|
+
d["tool_distribution"] = json.dumps(dist)
|
|
61
|
+
hour = str(datetime.now(timezone.utc).hour)
|
|
62
|
+
hours = json.loads(d["hour_distribution"])
|
|
63
|
+
hours[hour] = hours.get(hour, 0) + 1
|
|
64
|
+
d["hour_distribution"] = json.dumps(hours)
|
|
65
|
+
self._save(agent_id, d)
|
|
66
|
+
|
|
67
|
+
def record_feedback(self, agent_id: str, signal: str):
|
|
68
|
+
d = self._ensure(agent_id)
|
|
69
|
+
d["total_feedback_given"] += 1
|
|
70
|
+
key = f"feedback_{signal}"
|
|
71
|
+
if key in d:
|
|
72
|
+
d[key] += 1
|
|
73
|
+
good = d["feedback_useful"]
|
|
74
|
+
bad = d["feedback_misleading"] + d["feedback_incorrect"] + d["feedback_outdated"]
|
|
75
|
+
total = good + bad
|
|
76
|
+
d["feedback_quality"] = round(good / total, 3) if total > 0 else 0.5
|
|
77
|
+
if signal in ("misleading", "incorrect"):
|
|
78
|
+
d["consecutive_clean"] = 0
|
|
79
|
+
self._save(agent_id, d)
|
|
80
|
+
|
|
81
|
+
def record_invalidation(self, agent_id: str, count: int = 1):
|
|
82
|
+
d = self._ensure(agent_id)
|
|
83
|
+
d["invalidations_caused"] += count
|
|
84
|
+
d["consecutive_clean"] = 0
|
|
85
|
+
self._save(agent_id, d)
|
|
86
|
+
|
|
87
|
+
def increment_clean(self, agent_id: str):
|
|
88
|
+
d = self._ensure(agent_id)
|
|
89
|
+
d["consecutive_clean"] += 1
|
|
90
|
+
self._save(agent_id, d)
|
|
91
|
+
|
|
92
|
+
def _ensure(self, agent_id: str) -> Dict[str, Any]:
|
|
93
|
+
if agent_id not in self._memory:
|
|
94
|
+
self._memory[agent_id] = dict(_DEFAULT_PROFILE)
|
|
95
|
+
return self._memory[agent_id]
|
|
96
|
+
|
|
97
|
+
def _save(self, agent_id: str, data: Dict):
|
|
98
|
+
self._memory[agent_id] = data
|
|
99
|
+
if self._redis:
|
|
100
|
+
try:
|
|
101
|
+
self._redis.hset(f"{self._prefix}:{agent_id}", mapping={k: str(v) for k, v in data.items()})
|
|
102
|
+
except Exception:
|
|
103
|
+
pass
|