topos-node 1.2.0__tar.gz → 1.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {topos_node-1.2.0/topos_node.egg-info → topos_node-1.2.2}/PKG-INFO +1 -1
- {topos_node-1.2.0 → topos_node-1.2.2}/pyproject.toml +1 -1
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/__version__.py +2 -2
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/enrichment.py +10 -1
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/signal.py +45 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/ui_config.py +21 -1
- topos_node-1.2.2/topos/api/upgrades.py +30 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/app.py +15 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/control_plane_client.py +6 -2
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/config.py +23 -39
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/signal_features.py +55 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/entities/consolidation.py +185 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/entities/fact_materializer.py +8 -3
- topos_node-1.2.2/topos/features/entities/graph_enrichers.py +393 -0
- topos_node-1.2.2/topos/features/entities/graph_refresh.py +145 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/entities/resolver.py +23 -3
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/stats/definitions.py +5 -1
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/canonical_pipeline.py +10 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/protocol/handled_message_types.json +3 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/retrieval.py +47 -13
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/scope_registry.json +3 -1
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sources/install_service.py +4 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/__init__.py +6 -0
- topos_node-1.2.2/topos/storage/db/migrations/derivation_ledger_v1.py +44 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sync/client.py +6 -2
- topos_node-1.2.2/topos/upgrades/runner.py +339 -0
- {topos_node-1.2.0 → topos_node-1.2.2/topos_node.egg-info}/PKG-INFO +1 -1
- {topos_node-1.2.0 → topos_node-1.2.2}/topos_node.egg-info/SOURCES.txt +4 -0
- topos_node-1.2.0/topos/features/entities/graph_enrichers.py +0 -211
- {topos_node-1.2.0 → topos_node-1.2.2}/LICENSE +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/MANIFEST.in +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/README.md +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/setup.cfg +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/shared/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/shared/filtering.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/shared/schema_registry.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/tests/test_llm_integrations_storage.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/tests/test_protocol_handled_types.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/analytics/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/analytics/duckdb_adapter.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/analytics/messenger_communities.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/analytics/messenger_graph.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/analytics/messenger_labels.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/analytics/profiles.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/analytics/query_engine.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/analytics/raw_queries.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/analytics.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/app_registry.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/backup.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/compute_remote.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/data_commit.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/data_explorer_table_prefs.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/database_explorer.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/db.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/device.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/engine_run.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/enrichment_lab.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/filter_lab.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/health.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/home_chat.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/ingestion_api.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/ingestion_compat.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/ingestion_sources.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/intelligence_lifecycle.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/llm.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/local_mcp.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/messenger_analytics.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/privacy_disclose.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/query_api.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/runtime_bootstrap.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/sanitization_ollama_config.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/signal_extraction_config.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/source_install.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/source_scrub.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/sources.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/sources_overview.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/sync.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/uma_data.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/usage.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/api/user_identity.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/auth.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/canonicalization/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/canonicalization/mappers/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/canonicalization/mappers/base.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/canonicalization/mappers/browser_activity_mapper.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/canonicalization/mappers/chatgpt_mapper.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/canonicalization/mappers/demo_mappers.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/canonicalization/mappers/github_activity_mapper.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/canonicalization/mappers/grok_mapper.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/canonicalization/mappers/messenger_mapper.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/canonicalization/models.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/canonicalization/resolver.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/cli/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/cli/__main__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/cli/commands.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/config/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/config/sanitization_ollama.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/config/settings.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/config/signal_extraction.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/config/uvicorn_logging.json +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/contacts/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/contacts/identity.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/api_models.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/connection_resilience.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/device_helpers.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/errors.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/events.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/common.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/contacts_import.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/database_explorer.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/device.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/enrichment.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/enrichment_lab.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/filter_lab.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/home_chat.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/ingest.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/messages.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/messenger_analytics.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/query.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/registry.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/routines.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/sources.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/handlers/uma.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/logging.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/metrics.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/routine_access.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/startup_banner.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/state.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/table_layers.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/core/types.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/data_explorer_row_delete.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/data_explorer_table_prefs.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/data_explorer_tables.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/disclosure/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/disclosure/canonical_writer.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/disclosure/content_policy.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/disclosure/field_registry.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/disclosure/privacy_layer.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/disclosure/tier.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/backends/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/backends/base.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/backends/generative_prompts.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/backends/generative_response.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/backends/huggingface.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/backends/ollama.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/backends/openai.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/backends/openai_compatible.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/backends/redpill.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/backends/stub.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/client.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/engine.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/intake.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/memory_utils.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/model_cache.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/pipeline_memory.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/queue_manager.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/registration.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/result_formatter.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/router.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/scoped_token.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/tasks.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/transport.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/usage_guard.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/usage_observation.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/engine/validator.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/catalog.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/derived_tables.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/job_writer.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/base.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/_batch_limits.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/_engine_runner.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/availability_scores_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/brief_fallback.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/dimension_summary_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/embeddings_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/emo_27_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/entities_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/fact_extraction_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/goal_extraction_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/pii_redaction_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/relationship_edges_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/sentiment_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/statistics_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/timeline_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/topic_clusters_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/topics_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/url_classification_core.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/url_classification_enrichment_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/canonical/url_classification_signal_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/raw/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/raw/attachments_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/raw/language_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/raw/time_normalization_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/jobs/raw/tool_calls_job.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/model_overrides.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/models/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/models/manager.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/models/mvp_defaults.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/models/registry.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/models/versioning.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/orchestrator.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/processor.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/progress_bar.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/registry_contract.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/signal_derivation_config.json +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/source_overrides.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment/website_classifier.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment_lab/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment_lab/bundles.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment_lab/model_resolve.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment_lab/schema.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment_lab/service.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment_lab/store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/enrichment_lab/worker.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/evals/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/evals/enrichment_eval.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/entities/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/entities/dossier.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/entities/edges.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/entities/graph_search.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/entities/linking.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/entities/maintenance.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/entities/reads.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/facts/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/facts/extract.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/facts/llm_extract.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/facts/reads.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/facts/store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/fit/evaluator.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/fit/opportunity_types.json +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/lifecycle/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/lifecycle/derived_scrub.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/lifecycle/exclusions.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/lifecycle/gc.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/provenance/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/provenance/posture.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/provenance/roles.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/brief_canonical_loader.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/brief_ontology.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/brief_renderer.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/brief_schemas.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/chunking.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/cluster_labels.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/data_health.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/definitions/brief_ontologies.json +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/definitions/intentions.json +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/definitions/profile.json +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/definitions/relationships.json +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/definitions/schema/dimension_definition.schema.json +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/definitions/time.json +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/definitions/work.json +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/dimension_briefs.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/dimension_definition_loader.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/dimension_profiles.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/dimension_registry.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/embed_context.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/extraction/artifact_router.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/extraction/artifact_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/extraction/rule_extractors.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/graph_sanitize.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/hybrid_search.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/incremental_clustering.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/model_recommendations.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/query_embed_cache.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/schemas.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/service.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/signal_object_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/source_hydration.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/structured_signal.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/topic_clustering.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/typed_stores/aggregates.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/typed_stores/scope_materializer.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/vector_codec.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/vector_math.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/signal/vector_settings.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/stats/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/stats/engine.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/stats/fold.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/features/stats/insights.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/filter_lab/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/filter_lab/bundles.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/filter_lab/schema.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/filter_lab/service.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/filter_lab/store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/filter_lab/worker.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/home_chat/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/home_chat/schema.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/home_chat/store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/hosted_pool_lease.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/checkpoints/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/checkpoints/checkpoint_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/checkpoints/sqlite_checkpoint_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/inbox_crypto.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/inbox_drain.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/ingest_helpers.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/jobs.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/journal_location_fanout.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/journal_time_log_normalize.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/local_sync.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/log_preview.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/manager.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parser.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parsers/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parsers/base.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parsers/browser_parser.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parsers/calendar_parser.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parsers/chatgpt_conversation_flattener.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parsers/chatgpt_parser.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parsers/demo_file_parsers.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parsers/github_activity_parser.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parsers/grok_parser.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parsers/messenger_parser.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/parsers/voxterm_parser.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/progress.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/reprocess.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/sources/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/sources/base.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/sources/calendar.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/sources/chatgpt.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/sources/contact_importers.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/sources/grok.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/sources/imessage_reader.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/sources/signal_export_parser.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/sources/signal_reader.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/state_machine.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/triggers/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/triggers/file_trigger.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/triggers/sqlite_trigger.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/usage_inbox_dedupe.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/validation/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/validation/base.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/validation/schema_registry.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/ingestion/validation/schema_validator.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/lineage/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/lineage/provenance.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/lineage/tracker.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/llm_integrations_storage.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/mcp_stdio_proxy.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/observability/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/observability/alerts.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/observability/metrics.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/observability/tracing.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/openai_client.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/pipeline/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/pipeline/audit.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/pipeline/envelope.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/pipeline/stages.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/pipeline/stub_enqueue.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/projections/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/projections/vector_index/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/projections/vector_index/base.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/projections/vector_index/builders.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/projections/vector_index/health_checks.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/protocol/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/audit.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/ddr.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/disclosure.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/fingerprint.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/game_layer.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/grant_cache.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/inference.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/intent.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/manifest.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/manifest_validation.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/minimizer.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/negotiation.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/pipeline.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/planner.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/runtime.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/scope_registry_loader.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/session.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/session_utils.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/source_generation.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/turn_classifier.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/query/types.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/rate_limit.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/routines/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/routines/schema.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/routines/store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/runtime_update.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sanitization/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sanitization/nsfw_classifier.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sanitization/ollama_transforms.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sanitization/privacy_filter.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/scope_resolution.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/container.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/embeddings/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/embeddings/base.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/embeddings/local.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/embeddings/remote.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/interfaces.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/llm/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/llm/base.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/llm/openai.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/local.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/services/postgres.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sources/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sources/bundled_canonical_triples.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sources/canonical_signal_defaults.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sources/definitions.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sources/registry.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sources/runtime_install.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sources/scrub_attribution.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sources/scrub_service.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/startup_banner.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/adapters/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/adapters/factory.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/adapters/fakes.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/adapters/postgres/stores.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/adapters/protocols.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/adapters/sqlite/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/adapters/sqlite/stores.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/adapters/sqlite/vector_search.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/activity_tables.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/ai_chat/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/ai_chat/canonicalizer.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/ai_chat/mapper.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/ai_chat/model.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/ai_chat/tables.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/calendar_contacts_tables.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/canonical_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/conversations_tables.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/mapping_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/canonical/postgres.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/client.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/connection_tuning.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/activity_events_content_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/actor_role_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/canonical_disclosure_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/canonical_nsfw_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/entity_edges_validity_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/episodes_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/extraction_artifacts.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/journal_entries_duration_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/journal_entries_ends_at_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/journal_entries_people_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/journal_entries_place_name_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/journal_entries_starts_at_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/remediation_person_model.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/scope_source_generation.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/signal_dimension_backfill_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/signal_dimension_harness.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/signal_objects.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/signal_objects_period_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/signal_objects_updated_by_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/stage9_column_renames.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/vector_storage_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/vector_storage_v2.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/vector_storage_v3.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/vector_storage_v4.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_clusters_v2.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_entities_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_entity_review_v2.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_facts_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_lifecycle_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_mvp_phase0.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_mvp_phase1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_mvp_phase4_messages_cutover.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_mvp_phase5_topic_clusters.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_mvp_phase6_dimension_briefs.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_mvp_query_quality.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_mvp_topic_clusters_coordination.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_stats_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/migrations/wiki_timeline_v1.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/paths.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/postgres.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/schema.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/db/storage_breakdown.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/enrichment/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/enrichment/canonical_enrichment_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/enrichment/raw_enrichment_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/normalized/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/normalized/normalized_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/oplog/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/oplog/decision.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/oplog/oplog_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/oplog/postgres.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/person/identity_resolver.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/projections/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/projections/index_ops_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/projections/vector_index_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/raw/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/raw/browser_flat_tables.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/raw/file_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/raw/raw_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/raw/raw_tables_manager.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/raw/sqlite_raw_store.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/security/encryption.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/signal_identity.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/source_settings.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/storage/user_identity.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sync/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/sync_handlers.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/testing/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/testing/lifespan.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/uma_contact_enrichment.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/uma_filters.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/uma_resource_id.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/uma_rpt.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/upgrades/__init__.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/upgrades/manifests.json +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/utils/base_object.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos/websocket_client.py +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos_node.egg-info/dependency_links.txt +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos_node.egg-info/entry_points.txt +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos_node.egg-info/requires.txt +0 -0
- {topos_node-1.2.0 → topos_node-1.2.2}/topos_node.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "topos-node"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.2"
|
|
8
8
|
description = "Topos personal AI engine (FastAPI): local data, sync, control plane WebSocket client"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -672,7 +672,7 @@ def _load_canonical_messages_for_enrichment(
|
|
|
672
672
|
if title:
|
|
673
673
|
rec["content"] = str(title)
|
|
674
674
|
if not rec.get("event_at"):
|
|
675
|
-
for key in ("occurred_at", "
|
|
675
|
+
for key in ("occurred_at", "starts_at", "entry_at", "posted_at", "visited_at"):
|
|
676
676
|
if rec.get(key):
|
|
677
677
|
rec["event_at"] = rec[key]
|
|
678
678
|
break
|
|
@@ -1056,6 +1056,15 @@ async def _process_enrichment_core(
|
|
|
1056
1056
|
progress_callback=progress_callback,
|
|
1057
1057
|
)
|
|
1058
1058
|
|
|
1059
|
+
# Canonical jobs changed the derived layers even when the signal lane is
|
|
1060
|
+
# skipped below — mark the graph for a debounced rebuild either way.
|
|
1061
|
+
try:
|
|
1062
|
+
from ..features.entities.graph_refresh import mark_graph_dirty
|
|
1063
|
+
|
|
1064
|
+
mark_graph_dirty()
|
|
1065
|
+
except Exception: # noqa: BLE001 — refresh must never break enrichment
|
|
1066
|
+
pass
|
|
1067
|
+
|
|
1059
1068
|
# Manual processing is the deliberate counterpart of the automatic ingest
|
|
1060
1069
|
# path, so it must also run the signal-derivation lane (embeddings,
|
|
1061
1070
|
# clusters, facts, ...) that the ingest path skips for manual sources.
|
|
@@ -388,6 +388,51 @@ async def dismiss_entity_review(review_id: str, _api_key: str = Depends(require_
|
|
|
388
388
|
raise HTTPException(status_code=400, detail=str(exc))
|
|
389
389
|
|
|
390
390
|
|
|
391
|
+
class EntitySplitBody(BaseModel):
|
|
392
|
+
surface: str = Field(..., min_length=1, max_length=200)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
@router.post("/entities/{entity_id}/split")
|
|
396
|
+
async def split_entity_surface(
|
|
397
|
+
entity_id: str,
|
|
398
|
+
body: EntitySplitBody,
|
|
399
|
+
_api_key: str = Depends(require_api_key),
|
|
400
|
+
):
|
|
401
|
+
"""Owner unbind: split a surface's mentions out of this entity into a
|
|
402
|
+
fresh one, with a permanent no-bind guard so the resolver never re-merges
|
|
403
|
+
the pair (accidental-merge reversal; see consolidation.split_surface)."""
|
|
404
|
+
from ..features.entities.consolidation import split_surface
|
|
405
|
+
|
|
406
|
+
try:
|
|
407
|
+
return split_surface(_entities_conn(), entity_id, body.surface)
|
|
408
|
+
except LookupError as exc:
|
|
409
|
+
raise HTTPException(status_code=404, detail=str(exc))
|
|
410
|
+
except ValueError as exc:
|
|
411
|
+
raise HTTPException(status_code=400, detail=str(exc))
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class EntityMergeBody(BaseModel):
|
|
415
|
+
absorb_entity_id: str = Field(..., min_length=1, max_length=80)
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
@router.post("/entities/{entity_id}/merge")
|
|
419
|
+
async def merge_entity_into(
|
|
420
|
+
entity_id: str,
|
|
421
|
+
body: EntityMergeBody,
|
|
422
|
+
_api_key: str = Depends(require_api_key),
|
|
423
|
+
):
|
|
424
|
+
"""Owner link: merge another entity into this one (drawer-driven inverse
|
|
425
|
+
of split; see consolidation.merge_entity_pair)."""
|
|
426
|
+
from ..features.entities.consolidation import merge_entity_pair
|
|
427
|
+
|
|
428
|
+
try:
|
|
429
|
+
return merge_entity_pair(_entities_conn(), entity_id, body.absorb_entity_id)
|
|
430
|
+
except LookupError as exc:
|
|
431
|
+
raise HTTPException(status_code=404, detail=str(exc))
|
|
432
|
+
except ValueError as exc:
|
|
433
|
+
raise HTTPException(status_code=400, detail=str(exc))
|
|
434
|
+
|
|
435
|
+
|
|
391
436
|
@router.post("/entities/{entity_id}/exclude")
|
|
392
437
|
async def exclude_entity_from_intelligence(
|
|
393
438
|
entity_id: str,
|
|
@@ -24,8 +24,11 @@ ALLOWED_WIDGETS = {
|
|
|
24
24
|
MAX_PINNED = 3
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
GRAPH_WINDOW_MAX_DAYS = 3650
|
|
28
|
+
|
|
29
|
+
|
|
27
30
|
def _default_ui_config() -> dict[str, Any]:
|
|
28
|
-
return {"version": 1, "topbar": {"pinnedAnalytics": []}}
|
|
31
|
+
return {"version": 1, "topbar": {"pinnedAnalytics": []}, "graph": {}}
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
def _normalize_ui_config(value: Any) -> dict[str, Any]:
|
|
@@ -35,7 +38,24 @@ def _normalize_ui_config(value: Any) -> dict[str, Any]:
|
|
|
35
38
|
out = {
|
|
36
39
|
"version": int(value.get("version", 1)) if str(value.get("version", "")).isdigit() else 1,
|
|
37
40
|
"topbar": {"pinnedAnalytics": []},
|
|
41
|
+
"graph": {},
|
|
38
42
|
}
|
|
43
|
+
# Graph view prefs: the temporal window is personal (data volume/velocity
|
|
44
|
+
# dependent — 2 days for some owners, 2 months for others). timeWindowDays
|
|
45
|
+
# is a whole-day count; explicit null = all time; absent = unset.
|
|
46
|
+
graph = value.get("graph")
|
|
47
|
+
if isinstance(graph, dict) and "timeWindowDays" in graph:
|
|
48
|
+
window = graph.get("timeWindowDays")
|
|
49
|
+
if window is None:
|
|
50
|
+
out["graph"]["timeWindowDays"] = None
|
|
51
|
+
elif isinstance(window, (int, float)) and not isinstance(window, bool):
|
|
52
|
+
days = int(window)
|
|
53
|
+
if 1 <= days <= GRAPH_WINDOW_MAX_DAYS:
|
|
54
|
+
out["graph"]["timeWindowDays"] = days
|
|
55
|
+
if isinstance(graph, dict):
|
|
56
|
+
mode = graph.get("nodeColorMode")
|
|
57
|
+
if mode in ("type", "community"):
|
|
58
|
+
out["graph"]["nodeColorMode"] = mode
|
|
39
59
|
topbar = value.get("topbar")
|
|
40
60
|
pinned = topbar.get("pinnedAnalytics") if isinstance(topbar, dict) else []
|
|
41
61
|
if not isinstance(pinned, list):
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Upgrade status surface: what the runner + graph refresher are doing.
|
|
2
|
+
|
|
3
|
+
Read-only. The UI shows "Upgrading your intelligence…" instead of a
|
|
4
|
+
mysteriously thin graph while re-derivation runs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any, Dict
|
|
10
|
+
|
|
11
|
+
from fastapi import APIRouter, Depends
|
|
12
|
+
|
|
13
|
+
from ..auth import require_api_key
|
|
14
|
+
|
|
15
|
+
router = APIRouter()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _status_payload() -> Dict[str, Any]:
|
|
19
|
+
from ..core.state import get_db_connection
|
|
20
|
+
from ..features.entities.graph_refresh import status as refresh_status
|
|
21
|
+
from ..upgrades.runner import runner_status
|
|
22
|
+
|
|
23
|
+
conn = get_db_connection()
|
|
24
|
+
upgrade = runner_status(conn) if conn is not None else {"enabled": False, "error": "no database"}
|
|
25
|
+
return {"upgrade": upgrade, "graph_refresh": refresh_status()}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@router.get("/v1/upgrade/status", dependencies=[Depends(require_api_key)])
|
|
29
|
+
async def get_upgrade_status() -> Dict[str, Any]:
|
|
30
|
+
return _status_payload()
|
|
@@ -45,6 +45,7 @@ from .api import (
|
|
|
45
45
|
home_chat as home_chat_routes,
|
|
46
46
|
privacy_disclose as privacy_disclose_routes,
|
|
47
47
|
signal as signal_routes,
|
|
48
|
+
upgrades as upgrades_routes,
|
|
48
49
|
)
|
|
49
50
|
from .config.settings import settings
|
|
50
51
|
from .core.logging import align_uvicorn_loggers, configure_logging
|
|
@@ -93,6 +94,7 @@ app.add_middleware(
|
|
|
93
94
|
)
|
|
94
95
|
|
|
95
96
|
app.include_router(health_routes.router)
|
|
97
|
+
app.include_router(upgrades_routes.router)
|
|
96
98
|
app.include_router(local_mcp_routes.router)
|
|
97
99
|
app.include_router(llm_routes.router)
|
|
98
100
|
app.include_router(db_routes.router)
|
|
@@ -157,6 +159,19 @@ async def startup_event() -> None:
|
|
|
157
159
|
logger.info("Stage 9 migrations applied at startup: %d renames", len(result["applied"]))
|
|
158
160
|
except Exception as e:
|
|
159
161
|
logger.debug("Stage 9 migrations at startup (non-fatal): %s", e)
|
|
162
|
+
# Upgrade runner: compare the stamped derivation baseline against the
|
|
163
|
+
# shipped version and re-derive what this release invalidated (manifest in
|
|
164
|
+
# topos/upgrades). Heavy steps run in a daemon thread; the node serves
|
|
165
|
+
# stale derived data meanwhile. Fresh installs stamp-and-skip.
|
|
166
|
+
try:
|
|
167
|
+
from .core.state import get_db_connection as _get_conn_for_upgrades
|
|
168
|
+
from .upgrades.runner import start_background as _start_upgrades
|
|
169
|
+
|
|
170
|
+
_upgrade_conn = state.db_conn if state.db_conn is not None else _get_conn_for_upgrades()
|
|
171
|
+
if _upgrade_conn is not None:
|
|
172
|
+
_start_upgrades(_upgrade_conn)
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.warning("Upgrade runner at startup failed (non-fatal): %s", e)
|
|
160
175
|
try:
|
|
161
176
|
from .sources import install_service
|
|
162
177
|
|
|
@@ -141,7 +141,8 @@ class ControlPlaneClient:
|
|
|
141
141
|
try:
|
|
142
142
|
await asyncio.wait_for(self._ready.wait(), timeout=max(0.1, timeout))
|
|
143
143
|
return True
|
|
144
|
-
except TimeoutError:
|
|
144
|
+
except (TimeoutError, asyncio.TimeoutError):
|
|
145
|
+
# Py3.10: asyncio.TimeoutError is not builtins.TimeoutError.
|
|
145
146
|
return False
|
|
146
147
|
|
|
147
148
|
async def stop(self) -> None:
|
|
@@ -253,7 +254,10 @@ class ControlPlaneClient:
|
|
|
253
254
|
async def _wait_for_stop_or_timeout(self, timeout_s: float) -> None:
|
|
254
255
|
try:
|
|
255
256
|
await asyncio.wait_for(self._stop.wait(), timeout=timeout_s)
|
|
256
|
-
except TimeoutError:
|
|
257
|
+
except (TimeoutError, asyncio.TimeoutError):
|
|
258
|
+
# Expected path: backoff elapsed. Must not escape — on Py3.10
|
|
259
|
+
# asyncio.TimeoutError is distinct from builtins.TimeoutError and
|
|
260
|
+
# would otherwise kill the reconnect loop task.
|
|
257
261
|
return
|
|
258
262
|
|
|
259
263
|
async def _schedule_inbound_message(self, ws, data: Dict[str, Any]) -> None:
|
|
@@ -21,45 +21,15 @@ from .registry import handles
|
|
|
21
21
|
|
|
22
22
|
UI_CONFIG_KEY = "ui_config"
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
MAX_PINNED_WIDGETS = 3
|
|
36
|
-
|
|
37
|
-
def _default_ui_config() -> Dict[str, Any]:
|
|
38
|
-
return {"version": 1, "topbar": {"pinnedAnalytics": []}}
|
|
39
|
-
|
|
40
|
-
def _normalize_ui_config(value: Any) -> Dict[str, Any]:
|
|
41
|
-
if not isinstance(value, dict):
|
|
42
|
-
return _default_ui_config()
|
|
43
|
-
out: Dict[str, Any] = {
|
|
44
|
-
"version": int(value.get("version", 1)) if str(value.get("version", "")).isdigit() else 1,
|
|
45
|
-
"topbar": {"pinnedAnalytics": []},
|
|
46
|
-
}
|
|
47
|
-
topbar = value.get("topbar")
|
|
48
|
-
pinned = topbar.get("pinnedAnalytics") if isinstance(topbar, dict) else []
|
|
49
|
-
if not isinstance(pinned, list):
|
|
50
|
-
return out
|
|
51
|
-
seen: set[str] = set()
|
|
52
|
-
final: List[str] = []
|
|
53
|
-
for item in pinned:
|
|
54
|
-
wid = str(item or "").strip()
|
|
55
|
-
if not wid or wid in seen or wid not in ALLOWED_PINNED_WIDGETS:
|
|
56
|
-
continue
|
|
57
|
-
seen.add(wid)
|
|
58
|
-
final.append(wid)
|
|
59
|
-
if len(final) >= MAX_PINNED_WIDGETS:
|
|
60
|
-
break
|
|
61
|
-
out["topbar"]["pinnedAnalytics"] = final
|
|
62
|
-
return out
|
|
24
|
+
# SINGLE SOURCE: this module used to keep its own copy of the ui-config
|
|
25
|
+
# normalizer, which silently drifted — the HTTP endpoint accepted new fields
|
|
26
|
+
# (graph.timeWindowDays) that this WS path (the CP proxy route) stripped.
|
|
27
|
+
from ...api.ui_config import ( # noqa: E402
|
|
28
|
+
ALLOWED_WIDGETS as ALLOWED_PINNED_WIDGETS,
|
|
29
|
+
MAX_PINNED as MAX_PINNED_WIDGETS,
|
|
30
|
+
_default_ui_config,
|
|
31
|
+
_normalize_ui_config,
|
|
32
|
+
)
|
|
63
33
|
|
|
64
34
|
@handles("migrate_browser_plugin_app_id")
|
|
65
35
|
async def handle_migrate_browser_plugin_app_id(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
@@ -349,6 +319,20 @@ async def handle_delete_signal_extraction_config(message: Dict[str, Any]) -> Opt
|
|
|
349
319
|
# ---- Exposure-profile visibility (P1.5): the owner's toggle for whether the
|
|
350
320
|
# labeled "what you've been exposed to" profile surfaces on interest/identity
|
|
351
321
|
# answers. Default ON; the engine read path is config.settings.exposure_profile_visible.
|
|
322
|
+
@handles("get_upgrade_status")
|
|
323
|
+
async def handle_get_upgrade_status(message):
|
|
324
|
+
"""Upgrade runner + graph-refresh status (read-only; UI progress banner)."""
|
|
325
|
+
req_id = message.get("id")
|
|
326
|
+
if not req_id:
|
|
327
|
+
return None
|
|
328
|
+
try:
|
|
329
|
+
from ...api.upgrades import _status_payload
|
|
330
|
+
|
|
331
|
+
return {"id": req_id, "status": "ok", "payload": _status_payload()}
|
|
332
|
+
except Exception as exc: # noqa: BLE001
|
|
333
|
+
return {"id": req_id, "status": "error", "error": str(exc)}
|
|
334
|
+
|
|
335
|
+
|
|
352
336
|
@handles("get_exposure_profile_config")
|
|
353
337
|
async def handle_get_exposure_profile_config(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
354
338
|
req_id = message.get("id")
|
|
@@ -651,6 +651,61 @@ async def handle_signal_entity_review_action(message: Dict[str, Any]) -> Optiona
|
|
|
651
651
|
return {"id": req_id, "status": "error", "error": str(exc)}
|
|
652
652
|
|
|
653
653
|
|
|
654
|
+
@handles("signal_entity_split")
|
|
655
|
+
async def handle_signal_entity_split(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
656
|
+
req_id = message.get("id")
|
|
657
|
+
if not req_id:
|
|
658
|
+
return None
|
|
659
|
+
payload = message.get("payload") or {}
|
|
660
|
+
entity_id = str(payload.get("entity_id") or "").strip()
|
|
661
|
+
surface = str(payload.get("surface") or "").strip()
|
|
662
|
+
if not entity_id or not surface:
|
|
663
|
+
return {"id": req_id, "status": "error", "error": "entity_id and surface required", "code": 400}
|
|
664
|
+
try:
|
|
665
|
+
from ...features.entities.consolidation import split_surface
|
|
666
|
+
|
|
667
|
+
conn = hub.get_db_connection()
|
|
668
|
+
return {"id": req_id, "status": "ok", "payload": split_surface(conn, entity_id, surface)}
|
|
669
|
+
except LookupError as exc:
|
|
670
|
+
return {"id": req_id, "status": "error", "error": str(exc), "code": 404}
|
|
671
|
+
except ValueError as exc:
|
|
672
|
+
return {"id": req_id, "status": "error", "error": str(exc), "code": 400}
|
|
673
|
+
except Exception as exc: # noqa: BLE001
|
|
674
|
+
return {"id": req_id, "status": "error", "error": str(exc)}
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
@handles("signal_entity_merge")
|
|
678
|
+
async def handle_signal_entity_merge(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
679
|
+
req_id = message.get("id")
|
|
680
|
+
if not req_id:
|
|
681
|
+
return None
|
|
682
|
+
payload = message.get("payload") or {}
|
|
683
|
+
entity_id = str(payload.get("entity_id") or "").strip()
|
|
684
|
+
absorb_entity_id = str(payload.get("absorb_entity_id") or "").strip()
|
|
685
|
+
if not entity_id or not absorb_entity_id:
|
|
686
|
+
return {
|
|
687
|
+
"id": req_id,
|
|
688
|
+
"status": "error",
|
|
689
|
+
"error": "entity_id and absorb_entity_id required",
|
|
690
|
+
"code": 400,
|
|
691
|
+
}
|
|
692
|
+
try:
|
|
693
|
+
from ...features.entities.consolidation import merge_entity_pair
|
|
694
|
+
|
|
695
|
+
conn = hub.get_db_connection()
|
|
696
|
+
return {
|
|
697
|
+
"id": req_id,
|
|
698
|
+
"status": "ok",
|
|
699
|
+
"payload": merge_entity_pair(conn, entity_id, absorb_entity_id),
|
|
700
|
+
}
|
|
701
|
+
except LookupError as exc:
|
|
702
|
+
return {"id": req_id, "status": "error", "error": str(exc), "code": 404}
|
|
703
|
+
except ValueError as exc:
|
|
704
|
+
return {"id": req_id, "status": "error", "error": str(exc), "code": 400}
|
|
705
|
+
except Exception as exc: # noqa: BLE001
|
|
706
|
+
return {"id": req_id, "status": "error", "error": str(exc)}
|
|
707
|
+
|
|
708
|
+
|
|
654
709
|
@handles("signal_exclude_entity")
|
|
655
710
|
async def handle_signal_exclude_entity(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
656
711
|
req_id = message.get("id")
|
|
@@ -18,6 +18,7 @@ Dismissed pairs are never re-proposed.
|
|
|
18
18
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
21
|
+
import json
|
|
21
22
|
import logging
|
|
22
23
|
import sqlite3
|
|
23
24
|
import uuid
|
|
@@ -386,3 +387,187 @@ def resolve_review(
|
|
|
386
387
|
conn.commit()
|
|
387
388
|
refresh_dossiers(conn)
|
|
388
389
|
return {"review_id": review_id, "status": "approved", "kept": str(candidate_id), "absorbed": str(subject_id)}
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def split_surface(conn: sqlite3.Connection, entity_id: str, surface: str) -> Dict[str, Any]:
|
|
393
|
+
"""Owner unbind: split a surface's mentions OUT of an entity, permanently.
|
|
394
|
+
|
|
395
|
+
The reverse of an accidental merge/bind (e.g. the resolver's unique-contact
|
|
396
|
+
tier binding "Claire" to the contact "Claire Duncombe"):
|
|
397
|
+
|
|
398
|
+
* mentions whose surface_text normalizes to ``surface`` move to a fresh
|
|
399
|
+
entity named after the surface (created only when mentions exist);
|
|
400
|
+
* a matching alias is removed from the source entity;
|
|
401
|
+
* a permanent ``no_bind`` guard row is written — the resolver consults it
|
|
402
|
+
so this surface can never resolve back to the source entity (token-
|
|
403
|
+
subset names score 1.0, so every tier would otherwise re-merge them);
|
|
404
|
+
* both mention counts recount. Edges/dossiers correct on the next graph
|
|
405
|
+
rebuild.
|
|
406
|
+
"""
|
|
407
|
+
from .resolver import EntityResolver, normalize_name
|
|
408
|
+
|
|
409
|
+
row = conn.execute(
|
|
410
|
+
"SELECT canonical_name, normalized_name, entity_type, aliases_json "
|
|
411
|
+
"FROM entities WHERE entity_id=?",
|
|
412
|
+
(str(entity_id),),
|
|
413
|
+
).fetchone()
|
|
414
|
+
if row is None:
|
|
415
|
+
raise LookupError(f"entity not found: {entity_id}")
|
|
416
|
+
canonical_name, normalized_name, entity_type, aliases_json = row
|
|
417
|
+
|
|
418
|
+
normalized = normalize_name(surface)
|
|
419
|
+
if not normalized:
|
|
420
|
+
raise ValueError(f"unresolvable surface: {surface!r}")
|
|
421
|
+
if normalized == str(normalized_name):
|
|
422
|
+
raise ValueError(
|
|
423
|
+
"surface is the entity's own name — use exclude to stop tracking it entirely"
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
# Move matching mentions to a fresh entity.
|
|
427
|
+
mention_rows = conn.execute(
|
|
428
|
+
"SELECT mention_id, surface_text FROM entity_mentions WHERE entity_id=?",
|
|
429
|
+
(str(entity_id),),
|
|
430
|
+
).fetchall()
|
|
431
|
+
moved_ids = [
|
|
432
|
+
str(mid) for mid, stext in mention_rows if normalize_name(str(stext or "")) == normalized
|
|
433
|
+
]
|
|
434
|
+
|
|
435
|
+
new_entity_id: Optional[str] = None
|
|
436
|
+
if moved_ids:
|
|
437
|
+
resolver = EntityResolver(conn)
|
|
438
|
+
new_entity_id = resolver._create_entity(str(surface).strip(), str(entity_type))
|
|
439
|
+
placeholders = ",".join("?" for _ in moved_ids)
|
|
440
|
+
conn.execute(
|
|
441
|
+
f"UPDATE entity_mentions SET entity_id=? WHERE mention_id IN ({placeholders})",
|
|
442
|
+
(new_entity_id, *moved_ids),
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
# Remove a matching alias from the source entity.
|
|
446
|
+
alias_removed = False
|
|
447
|
+
try:
|
|
448
|
+
aliases = [a for a in (json.loads(aliases_json or "[]")) if isinstance(a, str)]
|
|
449
|
+
except json.JSONDecodeError:
|
|
450
|
+
aliases = []
|
|
451
|
+
kept_aliases = [a for a in aliases if normalize_name(a) != normalized]
|
|
452
|
+
if len(kept_aliases) != len(aliases):
|
|
453
|
+
alias_removed = True
|
|
454
|
+
conn.execute(
|
|
455
|
+
"UPDATE entities SET aliases_json=?, updated_at=datetime('now') WHERE entity_id=?",
|
|
456
|
+
(json.dumps(kept_aliases), str(entity_id)),
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
# Permanent guard: this surface never binds to the source entity again.
|
|
460
|
+
conn.execute(
|
|
461
|
+
"""
|
|
462
|
+
INSERT INTO entity_review
|
|
463
|
+
(review_id, surface_text, candidate_entity_id, score, record_id,
|
|
464
|
+
status, created_at, kind, subject_entity_id, reason)
|
|
465
|
+
VALUES (?, ?, ?, 1.0, NULL, 'approved', datetime('now'), 'no_bind', ?, 'owner split')
|
|
466
|
+
""",
|
|
467
|
+
(f"rev_{uuid.uuid4().hex[:16]}", normalized, str(entity_id), new_entity_id),
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
# Recount both sides.
|
|
471
|
+
for eid in filter(None, [str(entity_id), new_entity_id]):
|
|
472
|
+
conn.execute(
|
|
473
|
+
"UPDATE entities SET mention_count = ("
|
|
474
|
+
" SELECT COUNT(*) FROM entity_mentions m WHERE m.entity_id = entities.entity_id"
|
|
475
|
+
"), updated_at=datetime('now') WHERE entity_id=?",
|
|
476
|
+
(eid,),
|
|
477
|
+
)
|
|
478
|
+
conn.commit()
|
|
479
|
+
|
|
480
|
+
return {
|
|
481
|
+
"entity_id": str(entity_id),
|
|
482
|
+
"surface": str(surface).strip(),
|
|
483
|
+
"new_entity_id": new_entity_id,
|
|
484
|
+
"mentions_moved": len(moved_ids),
|
|
485
|
+
"alias_removed": alias_removed,
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def merge_entity_pair(
|
|
490
|
+
conn: sqlite3.Connection, keep_id: str, absorb_id: str
|
|
491
|
+
) -> Dict[str, Any]:
|
|
492
|
+
"""Owner link: merge absorb_id into keep_id (drawer-driven inverse of split).
|
|
493
|
+
|
|
494
|
+
Reuses EntityResolver.merge_entities for mentions/aliases/edges, then clears
|
|
495
|
+
any no_bind guards between the pair so an explicit re-link after Unlink
|
|
496
|
+
works. Pending reviews that touched the absorbed entity are marked stale.
|
|
497
|
+
"""
|
|
498
|
+
from .dossier import refresh_dossiers
|
|
499
|
+
from .resolver import EntityResolver
|
|
500
|
+
|
|
501
|
+
keep_id = str(keep_id).strip()
|
|
502
|
+
absorb_id = str(absorb_id).strip()
|
|
503
|
+
if not keep_id or not absorb_id:
|
|
504
|
+
raise ValueError("keep_id and absorb_id are required")
|
|
505
|
+
if keep_id == absorb_id:
|
|
506
|
+
raise ValueError("cannot merge an entity into itself")
|
|
507
|
+
|
|
508
|
+
keep_row = conn.execute(
|
|
509
|
+
"SELECT entity_type, canonical_name FROM entities WHERE entity_id=?",
|
|
510
|
+
(keep_id,),
|
|
511
|
+
).fetchone()
|
|
512
|
+
absorb_row = conn.execute(
|
|
513
|
+
"SELECT entity_type, canonical_name FROM entities WHERE entity_id=?",
|
|
514
|
+
(absorb_id,),
|
|
515
|
+
).fetchone()
|
|
516
|
+
if keep_row is None:
|
|
517
|
+
raise LookupError(f"entity not found: {keep_id}")
|
|
518
|
+
if absorb_row is None:
|
|
519
|
+
raise LookupError(f"entity not found: {absorb_id}")
|
|
520
|
+
if str(keep_row[0]) != str(absorb_row[0]):
|
|
521
|
+
raise ValueError(
|
|
522
|
+
f"entity types must match (keep={keep_row[0]!r}, absorb={absorb_row[0]!r})"
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
mentions_moved = int(
|
|
526
|
+
conn.execute(
|
|
527
|
+
"SELECT COUNT(*) FROM entity_mentions WHERE entity_id=?",
|
|
528
|
+
(absorb_id,),
|
|
529
|
+
).fetchone()[0]
|
|
530
|
+
)
|
|
531
|
+
absorb_name = str(absorb_row[1] or "")
|
|
532
|
+
|
|
533
|
+
# Clear no_bind guards between this pair (either direction) so the owner
|
|
534
|
+
# can reverse a prior Unlink by explicitly linking again.
|
|
535
|
+
conn.execute(
|
|
536
|
+
"""
|
|
537
|
+
DELETE FROM entity_review
|
|
538
|
+
WHERE kind='no_bind'
|
|
539
|
+
AND (
|
|
540
|
+
(candidate_entity_id=? AND subject_entity_id=?)
|
|
541
|
+
OR (candidate_entity_id=? AND subject_entity_id=?)
|
|
542
|
+
)
|
|
543
|
+
""",
|
|
544
|
+
(keep_id, absorb_id, absorb_id, keep_id),
|
|
545
|
+
)
|
|
546
|
+
# Pending reviews that referenced the absorbed entity are now stale.
|
|
547
|
+
conn.execute(
|
|
548
|
+
"""
|
|
549
|
+
UPDATE entity_review SET status='stale'
|
|
550
|
+
WHERE status='pending'
|
|
551
|
+
AND (subject_entity_id=? OR candidate_entity_id=?)
|
|
552
|
+
""",
|
|
553
|
+
(absorb_id, absorb_id),
|
|
554
|
+
)
|
|
555
|
+
|
|
556
|
+
resolver = EntityResolver(conn)
|
|
557
|
+
resolver.merge_entities(keep_id, absorb_id)
|
|
558
|
+
|
|
559
|
+
# Also drop any leftover no_bind rows that still point at the deleted
|
|
560
|
+
# absorb id as the blocked candidate (surface → absorb).
|
|
561
|
+
conn.execute(
|
|
562
|
+
"DELETE FROM entity_review WHERE kind='no_bind' AND candidate_entity_id=?",
|
|
563
|
+
(absorb_id,),
|
|
564
|
+
)
|
|
565
|
+
conn.commit()
|
|
566
|
+
refresh_dossiers(conn)
|
|
567
|
+
|
|
568
|
+
return {
|
|
569
|
+
"kept": keep_id,
|
|
570
|
+
"absorbed": absorb_id,
|
|
571
|
+
"absorbed_name": absorb_name,
|
|
572
|
+
"mentions_moved": mentions_moved,
|
|
573
|
+
}
|
|
@@ -100,11 +100,14 @@ def _upsert_materialized_edge(
|
|
|
100
100
|
source_object_id: str,
|
|
101
101
|
asserted_by: Optional[str] = None,
|
|
102
102
|
actor_role: Optional[str] = None,
|
|
103
|
+
last_event_at: Optional[str] = None,
|
|
103
104
|
) -> None:
|
|
104
105
|
"""Idempotent directed edge with a materialized marker + carried validity.
|
|
105
106
|
|
|
106
107
|
``actor_role`` overrides the asserted_by-derived tier — used by enrichers
|
|
107
108
|
whose provenance isn't an assertion (presence, participation, witnessing).
|
|
109
|
+
``last_event_at`` defaults to valid_from — pass explicitly when the edge's
|
|
110
|
+
evidence spans a window (e.g. a goal recurring across months).
|
|
108
111
|
"""
|
|
109
112
|
if not src or not dst or src == dst:
|
|
110
113
|
return
|
|
@@ -126,6 +129,7 @@ def _upsert_materialized_edge(
|
|
|
126
129
|
""",
|
|
127
130
|
(src, dst, edge_type),
|
|
128
131
|
).fetchone()
|
|
132
|
+
effective_last = last_event_at or valid_from
|
|
129
133
|
if row is None:
|
|
130
134
|
conn.execute(
|
|
131
135
|
"""
|
|
@@ -137,17 +141,18 @@ def _upsert_materialized_edge(
|
|
|
137
141
|
(
|
|
138
142
|
f"fmz_{uuid.uuid4().hex[:16]}",
|
|
139
143
|
src, dst, edge_type, float(weight),
|
|
140
|
-
|
|
144
|
+
effective_last, valid_from or _now_iso(), valid_to, meta,
|
|
141
145
|
),
|
|
142
146
|
)
|
|
143
147
|
else:
|
|
144
148
|
conn.execute(
|
|
145
149
|
"""
|
|
146
150
|
UPDATE entity_edges
|
|
147
|
-
SET weight=?, valid_from=?, valid_to=?, metadata_json=?,
|
|
151
|
+
SET weight=?, valid_from=?, valid_to=?, last_event_at=?, metadata_json=?,
|
|
152
|
+
updated_at=datetime('now')
|
|
148
153
|
WHERE edge_id=?
|
|
149
154
|
""",
|
|
150
|
-
(float(weight), valid_from, valid_to, meta, row[0]),
|
|
155
|
+
(float(weight), valid_from, valid_to, effective_last, meta, row[0]),
|
|
151
156
|
)
|
|
152
157
|
|
|
153
158
|
|