topos-node 1.3.7__tar.gz → 1.3.9__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.3.7/topos_node.egg-info → topos_node-1.3.9}/PKG-INFO +1 -1
- {topos_node-1.3.7 → topos_node-1.3.9}/pyproject.toml +1 -1
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/__version__.py +2 -2
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/cli/tray.py +6 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/control_plane_client.py +20 -3
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/ingest.py +23 -13
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/state.py +22 -1
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/derivation_recovery.py +141 -55
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/attention_triage_job.py +26 -6
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/dimension_summary_job.py +57 -24
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/entities_job.py +9 -1
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/statistics_job.py +43 -21
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/timeline_job.py +12 -3
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/orchestrator.py +71 -10
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/graph_refresh.py +32 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/canonical_pipeline.py +24 -9
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/ingest_helpers.py +87 -52
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/manager.py +109 -94
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/reprocess.py +32 -25
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/pipeline/job_runner.py +32 -2
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/pipeline/job_store.py +23 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/scope_registry.json +12 -4
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/runtime_update.py +65 -2
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/__init__.py +88 -1
- topos_node-1.3.9/topos/storage/db/write_gate.py +688 -0
- topos_node-1.3.9/topos/testing/lifespan.py +69 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/upgrades/manifests.json +33 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/upgrades/runner.py +29 -3
- {topos_node-1.3.7 → topos_node-1.3.9/topos_node.egg-info}/PKG-INFO +1 -1
- topos_node-1.3.7/topos/storage/db/write_gate.py +0 -336
- topos_node-1.3.7/topos/testing/lifespan.py +0 -22
- {topos_node-1.3.7 → topos_node-1.3.9}/LICENSE +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/MANIFEST.in +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/README.md +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/setup.cfg +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/shared/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/shared/filtering.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/shared/schema_registry.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/tests/test_extensions.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/tests/test_llm_integrations_storage.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/tests/test_prewarm_first_run.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/tests/test_protocol_handled_types.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/tests/test_system_computer_name.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/analytics/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/analytics/duckdb_adapter.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/analytics/messenger_communities.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/analytics/messenger_graph.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/analytics/messenger_labels.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/analytics/profiles.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/analytics/query_engine.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/analytics/raw_queries.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/analytics.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/app_registry.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/backup.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/compute_remote.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/conversation_context.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/data_commit.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/data_explorer_table_prefs.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/database_explorer.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/db.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/device.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/engine_run.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/enrichment.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/enrichment_lab.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/facts_llm_config.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/filter_lab.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/health.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/home_chat.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/ingestion_api.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/ingestion_compat.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/ingestion_sources.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/intelligence_lifecycle.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/llm.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/local_mcp.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/messenger_analytics.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/privacy_disclose.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/query_api.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/runtime_bootstrap.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/sanitization_ollama_config.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/shell.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/signal.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/signal_extraction_config.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/source_install.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/source_scrub.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/sources.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/sources_overview.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/sync.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/tool_index.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/ui_config.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/uma_data.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/upgrades.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/usage.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/api/user_identity.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/app.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/auth.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/mappers/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/mappers/base.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/mappers/browser_activity_mapper.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/mappers/chatgpt_mapper.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/mappers/demo_mappers.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/mappers/documents_mapper.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/mappers/github_activity_mapper.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/mappers/google_calendar_mapper.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/mappers/grok_mapper.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/mappers/messenger_mapper.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/models.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/canonicalization/resolver.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/cli/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/cli/__main__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/cli/assets/topos_blk_rounded.png +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/cli/assets/topos_white.png +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/cli/commands.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/cli/reprocess_cmd.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/config/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/config/conversation_context_llm.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/config/facts_llm.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/config/model_packs.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/config/sanitization_ollama.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/config/settings.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/config/signal_extraction.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/config/uvicorn_logging.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/contacts/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/contacts/identity.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/api_models.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/connection_resilience.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/device_helpers.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/errors.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/events.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/common.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/complexity.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/config.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/contacts_import.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/database_explorer.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/device.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/enrichment.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/enrichment_lab.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/filter_lab.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/graph_query.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/home_chat.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/messages.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/messenger_analytics.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/query.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/registry.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/routines.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/signal_features.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/sources.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/timeline_daily.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/tool_index.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/handlers/uma.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/logging.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/metrics.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/routine_access.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/startup_banner.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/table_layers.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/core/types.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/data_explorer_row_delete.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/data_explorer_table_prefs.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/data_explorer_tables.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/disclosure/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/disclosure/canonical_writer.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/disclosure/content_policy.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/disclosure/field_registry.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/disclosure/privacy_layer.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/disclosure/tier.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/backends/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/backends/base.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/backends/generative_prompts.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/backends/generative_response.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/backends/huggingface.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/backends/ollama.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/backends/openai.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/backends/openai_compatible.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/backends/redpill.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/backends/stub.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/client.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/engine.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/intake.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/memory_utils.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/model_cache.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/pipeline_memory.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/queue_manager.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/registration.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/result_formatter.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/router.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/scoped_token.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/tasks.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/transport.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/usage_guard.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/usage_observation.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/engine/validator.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/catalog.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/derived_tables.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/job_writer.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/base.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/_batch_limits.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/_engine_runner.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/availability_scores_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/brief_fallback.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/complexity_snapshot_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/embeddings_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/emo_27_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/fact_extraction_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/goal_extraction_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/pii_redaction_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/relationship_edges_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/sentiment_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/topic_clusters_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/topics_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/url_classification_core.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/url_classification_enrichment_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/url_classification_signal_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/raw/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/raw/attachments_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/raw/language_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/raw/time_normalization_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/raw/tool_calls_job.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/model_overrides.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/models/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/models/manager.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/models/mvp_defaults.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/models/registry.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/models/versioning.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/pipeline_activity.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/processor.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/progress_bar.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/registry_contract.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/signal_derivation_config.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/source_overrides.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/website_classifier.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment_lab/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment_lab/bundles.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment_lab/model_resolve.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment_lab/schema.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment_lab/service.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment_lab/store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment_lab/worker.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/evals/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/evals/enrichment_eval.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/extensions.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/canonical.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/engine.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/entropy.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/focus.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/graph_metrics.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/influence.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/projection.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/snapshots.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/topic_merge.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/topics.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/topics_daily.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/complexity/volatility.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/affinity.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/affinity_owner.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/affinity_quality.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/consolidation.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/context_vectors.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/cypher.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/declared_mappings.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/dossier.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/edges.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/fact_materializer.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/graph_enrichers.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/graph_search.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/linking.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/maintenance.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/reads.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/rebuild_subprocess.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/entities/resolver.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/facts/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/facts/extract.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/facts/llm_extract.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/facts/reads.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/facts/store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/facts/verdicts.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/fit/evaluator.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/fit/opportunity_types.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/lifecycle/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/lifecycle/blackhole.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/lifecycle/blackhole_guard.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/lifecycle/blackhole_llm.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/lifecycle/blackhole_rebuild.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/lifecycle/derived_scrub.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/lifecycle/exclusions.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/lifecycle/gc.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/provenance/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/provenance/posture.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/provenance/roles.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/brief_canonical_loader.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/brief_ontology.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/brief_renderer.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/brief_schemas.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/chunking.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/cluster_labels.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/conversation_context.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/data_health.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/definitions/brief_ontologies.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/definitions/intentions.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/definitions/profile.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/definitions/relationships.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/definitions/schema/dimension_definition.schema.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/definitions/time.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/definitions/work.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/derived_spec.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/dimension_briefs.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/dimension_definition_loader.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/dimension_profiles.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/dimension_registry.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/embed_context.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/extraction/artifact_router.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/extraction/artifact_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/extraction/rule_extractors.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/graph_sanitize.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/hybrid_search.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/incremental_clustering.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/model_recommendations.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/query_embed_cache.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/schemas.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/service.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/signal_object_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/source_hydration.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/structured_signal.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/tool_index.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/topic_clustering.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/typed_stores/aggregates.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/typed_stores/commitments.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/typed_stores/rhythm.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/typed_stores/scope_materializer.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/vector_codec.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/vector_math.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/signal/vector_settings.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/stats/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/stats/definitions.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/stats/engine.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/stats/fold.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/stats/insights.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/timeline_projection.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/timeline_rollup.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/triage/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/triage/badges.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/triage/daily.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/triage/dashboard.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/triage/instruments.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/triage/intents.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/features/triage/readiness.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/filter_lab/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/filter_lab/bundles.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/filter_lab/schema.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/filter_lab/service.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/filter_lab/store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/filter_lab/worker.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/home_chat/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/home_chat/schema.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/home_chat/store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/hosted_pool_lease.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/checkpoints/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/checkpoints/checkpoint_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/checkpoints/sqlite_checkpoint_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/inbox_crypto.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/inbox_drain.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/jobs.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/journal_location_fanout.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/journal_time_log_normalize.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/local_sync.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/log_preview.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parser.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/base.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/browser_parser.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/calendar_parser.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/chatgpt_conversation_flattener.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/chatgpt_parser.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/demo_file_parsers.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/documents_parser.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/github_activity_parser.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/google_calendar_parser.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/grok_parser.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/messenger_parser.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/parsers/voxterm_parser.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/progress.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/sources/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/sources/base.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/sources/calendar.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/sources/chatgpt.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/sources/contact_importers.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/sources/grok.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/sources/imessage_reader.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/sources/signal_export_parser.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/sources/signal_reader.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/state_machine.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/triggers/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/triggers/file_trigger.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/triggers/sqlite_trigger.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/usage_inbox_dedupe.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/validation/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/validation/base.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/validation/schema_registry.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/ingestion/validation/schema_validator.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/lineage/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/lineage/provenance.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/lineage/tracker.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/llm_integrations_storage.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/mcp_stdio_proxy.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/observability/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/observability/alerts.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/observability/metrics.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/observability/tracing.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/openai_client.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/pipeline/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/pipeline/audit.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/pipeline/envelope.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/pipeline/stages.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/pipeline/stub_enqueue.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/projections/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/projections/vector_index/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/projections/vector_index/base.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/projections/vector_index/builders.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/projections/vector_index/health_checks.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/protocol/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/protocol/handled_message_types.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/audit.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/cohort_resolvers.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/ddr.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/disclosure.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/fingerprint.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/game_layer.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/grant_cache.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/inference.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/intent.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/manifest.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/manifest_validation.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/minimizer.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/negotiation.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/pipeline.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/planner.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/retrieval.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/runtime.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/scope_registry_loader.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/session.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/session_utils.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/source_generation.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/truth_facts.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/truth_prompts.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/turn_classifier.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/types.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/verify.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/query/verify_modes.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/rate_limit.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/routines/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/routines/schema.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/routines/store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/runtime_housekeeping.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/runtime_shutdown.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sanitization/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sanitization/nsfw_classifier.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sanitization/ollama_transforms.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sanitization/prewarm.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sanitization/privacy_filter.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/scope_resolution.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/container.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/embeddings/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/embeddings/base.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/embeddings/local.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/embeddings/remote.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/interfaces.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/llm/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/llm/base.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/llm/openai.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/local.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/services/postgres.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sources/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sources/bundled_canonical_triples.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sources/canonical_signal_defaults.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sources/definitions.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sources/install_service.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sources/registry.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sources/runtime_install.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sources/scrub_attribution.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sources/scrub_service.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/startup_banner.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/adapters/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/adapters/factory.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/adapters/fakes.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/adapters/postgres/stores.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/adapters/protocols.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/adapters/sqlite/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/adapters/sqlite/stores.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/adapters/sqlite/vector_search.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/activity_tables.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/ai_chat/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/ai_chat/canonicalizer.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/ai_chat/mapper.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/ai_chat/model.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/ai_chat/tables.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/calendar_contacts_tables.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/canonical_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/conversations_tables.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/mapping_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/canonical/postgres.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/client.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/connection_tuning.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/activity_events_content_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/actor_role_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/affinity_pair_labels_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/attention_triage_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/attention_triage_v2.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/backup.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/calendar_events_scheduling_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/canonical_address_book_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/canonical_disclosure_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/canonical_nsfw_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/complexity_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/conversation_context_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/derivation_ledger_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/documents_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/enrichment_spec_version_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/entity_affinity_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/entity_blackhole_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/entity_context_vectors_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/entity_context_vectors_v2.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/entity_edges_validity_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/entity_mentions_authored_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/episodes_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/extraction_artifacts.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/journal_entries_duration_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/journal_entries_ends_at_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/journal_entries_people_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/journal_entries_place_name_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/journal_entries_starts_at_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/message_emotions_role_backfill_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/message_emotions_role_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/pipeline_jobs_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/registry.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/registry_checksums.json +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/remediation_person_model.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/scope_source_generation.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/signal_dimension_backfill_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/signal_dimension_harness.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/signal_objects.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/signal_objects_period_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/signal_objects_updated_by_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/signal_summaries_drop_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/stage9_column_renames.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/vector_storage_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/vector_storage_v2.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/vector_storage_v3.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/vector_storage_v4.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_clusters_v2.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_entities_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_entity_review_v2.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_facts_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_lifecycle_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_mvp_phase0.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_mvp_phase1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_mvp_phase4_messages_cutover.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_mvp_phase5_topic_clusters.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_mvp_phase6_dimension_briefs.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_mvp_query_quality.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_mvp_topic_clusters_coordination.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_stats_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/migrations/wiki_timeline_v1.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/paths.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/postgres.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/schema.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/db/storage_breakdown.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/enrichment/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/enrichment/canonical_enrichment_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/enrichment/raw_enrichment_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/normalized/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/normalized/normalized_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/oplog/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/oplog/decision.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/oplog/oplog_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/oplog/postgres.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/person/identity_resolver.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/projections/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/projections/index_ops_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/projections/vector_index_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/raw/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/raw/browser_flat_tables.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/raw/file_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/raw/raw_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/raw/raw_tables_manager.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/raw/sqlite_raw_store.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/security/encryption.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/signal_identity.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/source_settings.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/storage/user_identity.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sync/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sync/client.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/sync_handlers.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/testing/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/uma_contact_enrichment.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/uma_filters.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/uma_resource_id.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/uma_rpt.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/upgrades/__init__.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/utils/base_object.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos/websocket_client.py +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos_node.egg-info/SOURCES.txt +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos_node.egg-info/dependency_links.txt +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos_node.egg-info/entry_points.txt +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/topos_node.egg-info/requires.txt +0 -0
- {topos_node-1.3.7 → topos_node-1.3.9}/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.3.
|
|
7
|
+
version = "1.3.9"
|
|
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"
|
|
@@ -329,6 +329,8 @@ class ToposTray:
|
|
|
329
329
|
items.append("Show Logs")
|
|
330
330
|
if self.update.get("applying"):
|
|
331
331
|
items.append("Installing update…")
|
|
332
|
+
elif self.update.get("last_result") == "failed":
|
|
333
|
+
items.append("Update failed — click to retry")
|
|
332
334
|
elif self.update.get("last_result") == "success":
|
|
333
335
|
items.append("Update installed — restart to finish")
|
|
334
336
|
elif self.update.get("available"):
|
|
@@ -368,6 +370,10 @@ class ToposTray:
|
|
|
368
370
|
items.append(pystray.MenuItem("Show Logs", self._show_logs))
|
|
369
371
|
if self.update.get("applying"):
|
|
370
372
|
items.append(pystray.MenuItem("Installing update…", None, enabled=False))
|
|
373
|
+
elif self.update.get("last_result") == "failed":
|
|
374
|
+
# Clickable: a failed update that renders nothing is
|
|
375
|
+
# indistinguishable from a click that never registered.
|
|
376
|
+
items.append(pystray.MenuItem("Update failed — click to retry", self._apply_update))
|
|
371
377
|
elif self.update.get("last_result") == "success":
|
|
372
378
|
items.append(pystray.MenuItem("Update installed — restart to finish", None, enabled=False))
|
|
373
379
|
elif self.update.get("available"):
|
|
@@ -29,6 +29,12 @@ logger = logging.getLogger("topos.control_plane_client")
|
|
|
29
29
|
|
|
30
30
|
# Lightweight CP RPCs that must stay responsive while ingest/enrichment runs.
|
|
31
31
|
# UI-critical reads and scheduler ops only — no ingest/enrichment writes.
|
|
32
|
+
#: Keepalive. The control plane's own uvicorn pings at 30s with a 120s
|
|
33
|
+
#: deadline; the node pings faster so IT notices a dead link first and
|
|
34
|
+
#: reconnects, rather than waiting to be evicted.
|
|
35
|
+
PING_INTERVAL_SECONDS = 20.0
|
|
36
|
+
PING_TIMEOUT_SECONDS = 20.0
|
|
37
|
+
|
|
32
38
|
_FAST_INBOUND_MESSAGE_TYPES = frozenset(
|
|
33
39
|
{
|
|
34
40
|
"healthcheck",
|
|
@@ -297,9 +303,20 @@ class ControlPlaneClient:
|
|
|
297
303
|
self.control_plane_url,
|
|
298
304
|
additional_headers=headers,
|
|
299
305
|
ssl=ssl_context,
|
|
300
|
-
#
|
|
301
|
-
#
|
|
302
|
-
|
|
306
|
+
# A real pong deadline, restored once the client got its own
|
|
307
|
+
# thread and loop. It was disabled because handlers ran here
|
|
308
|
+
# and could block long enough to miss pongs — that reason is
|
|
309
|
+
# gone: handlers now marshal to the app loop, so this loop is
|
|
310
|
+
# always free to answer and to notice silence.
|
|
311
|
+
#
|
|
312
|
+
# Disabling it turned every silent connection death into a
|
|
313
|
+
# permanent wedge: `async for raw in ws` blocked forever, no
|
|
314
|
+
# exception fired, no reconnect was scheduled, and the node
|
|
315
|
+
# reported "connected" while the control plane had no socket
|
|
316
|
+
# at all. Observed live 2026-08-08 — 25 minutes wedged, with
|
|
317
|
+
# the web app correctly showing the Topos offline.
|
|
318
|
+
ping_interval=PING_INTERVAL_SECONDS,
|
|
319
|
+
ping_timeout=PING_TIMEOUT_SECONDS,
|
|
303
320
|
) as ws:
|
|
304
321
|
self._ws = ws
|
|
305
322
|
self._set_state("connected")
|
|
@@ -215,20 +215,30 @@ async def handle_app_ingest(message: Dict[str, Any]) -> Optional[Dict[str, Any]]
|
|
|
215
215
|
len(errors),
|
|
216
216
|
errors[0].get("error"),
|
|
217
217
|
)
|
|
218
|
-
|
|
219
|
-
if db_conn and user_id:
|
|
218
|
+
if user_id:
|
|
220
219
|
resource_id = (payload.get("resource_id") or "").strip() or f"dataset:{user_id}:{dataset_id or 'default'}"
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
220
|
+
|
|
221
|
+
def _record_app_ingest_uma() -> None:
|
|
222
|
+
# record_uma_request takes the write gate (and on first use runs
|
|
223
|
+
# the uma_access_requests ensure/migrate DDL) — keep it off the
|
|
224
|
+
# event-loop thread. Own connection: get_db_connection is
|
|
225
|
+
# thread-local.
|
|
226
|
+
own = hub.get_db_connection()
|
|
227
|
+
if own is None:
|
|
228
|
+
return
|
|
229
|
+
hub.record_uma_request(
|
|
230
|
+
own,
|
|
231
|
+
owner_user_id=user_id,
|
|
232
|
+
resource_id=resource_id,
|
|
233
|
+
request_type="write",
|
|
234
|
+
endpoint="app_ingest",
|
|
235
|
+
requesting_user_id=(payload.get("requesting_user_id") or None),
|
|
236
|
+
app_id=payload.get("app_id"),
|
|
237
|
+
requesting_user_email=(payload.get("requesting_user_email") or "").strip() or None,
|
|
238
|
+
access_channel=(payload.get("access_channel") or "internal").strip() or "internal",
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
await asyncio.to_thread(_record_app_ingest_uma)
|
|
232
242
|
response_payload = {
|
|
233
243
|
"records_processed": processed,
|
|
234
244
|
"records_total": records_total,
|
|
@@ -183,7 +183,9 @@ from ..storage.db.write_gate import ( # noqa: E402
|
|
|
183
183
|
batched_writes,
|
|
184
184
|
commit_connection,
|
|
185
185
|
db_write_lock,
|
|
186
|
+
register_connection,
|
|
186
187
|
sqlite_retry_busy,
|
|
188
|
+
unregister_connection,
|
|
187
189
|
with_db_write,
|
|
188
190
|
)
|
|
189
191
|
|
|
@@ -285,6 +287,7 @@ def _get_thread_db_connection(resolved_path: str) -> Optional[sqlite3.Connection
|
|
|
285
287
|
_ensure_row_factory(cached)
|
|
286
288
|
return cached
|
|
287
289
|
except (sqlite3.ProgrammingError, sqlite3.OperationalError):
|
|
290
|
+
unregister_connection(cached)
|
|
288
291
|
try:
|
|
289
292
|
cached.close()
|
|
290
293
|
except Exception: # noqa: BLE001
|
|
@@ -293,6 +296,7 @@ def _get_thread_db_connection(resolved_path: str) -> Optional[sqlite3.Connection
|
|
|
293
296
|
_thread_state.path = None
|
|
294
297
|
elif cached is not None:
|
|
295
298
|
# Path changed under us (tests re-point DATABASE_PATH).
|
|
299
|
+
unregister_connection(cached)
|
|
296
300
|
try:
|
|
297
301
|
cached.close()
|
|
298
302
|
except Exception: # noqa: BLE001
|
|
@@ -314,6 +318,7 @@ def _get_thread_db_connection(resolved_path: str) -> Optional[sqlite3.Connection
|
|
|
314
318
|
except Exception as exc: # noqa: BLE001 — tuning is an optimization, not a gate
|
|
315
319
|
logger.debug("Per-thread connection tuning skipped: %s", exc)
|
|
316
320
|
|
|
321
|
+
register_connection(conn)
|
|
317
322
|
_thread_state.conn = conn
|
|
318
323
|
_thread_state.path = resolved_path
|
|
319
324
|
logger.debug(
|
|
@@ -329,6 +334,7 @@ def close_thread_db_connection() -> None:
|
|
|
329
334
|
conn = getattr(_thread_state, "conn", None)
|
|
330
335
|
if conn is None:
|
|
331
336
|
return
|
|
337
|
+
unregister_connection(conn)
|
|
332
338
|
try:
|
|
333
339
|
conn.close()
|
|
334
340
|
except Exception: # noqa: BLE001
|
|
@@ -371,6 +377,7 @@ def _get_owner_db_connection() -> Optional[sqlite3.Connection]:
|
|
|
371
377
|
try:
|
|
372
378
|
db_conn.execute("SELECT 1")
|
|
373
379
|
except (sqlite3.ProgrammingError, sqlite3.OperationalError):
|
|
380
|
+
unregister_connection(db_conn)
|
|
374
381
|
db_conn = None
|
|
375
382
|
_db_conn_path = None
|
|
376
383
|
else:
|
|
@@ -398,7 +405,10 @@ def _get_owner_db_connection() -> Optional[sqlite3.Connection]:
|
|
|
398
405
|
# it across queries) — closing under them is the use-after-close
|
|
399
406
|
# that SIGBUSed on 2026-08-08. Dropping the reference lets the last
|
|
400
407
|
# user finish; the handle closes (rolling back any open
|
|
401
|
-
# transaction) when its refcount reaches zero.
|
|
408
|
+
# transaction) when its refcount reaches zero. The watchdog is told
|
|
409
|
+
# here for the same reason it holds no strong reference: it must
|
|
410
|
+
# not be what keeps this handle (or its stale record) around.
|
|
411
|
+
unregister_connection(db_conn)
|
|
402
412
|
db_conn = None
|
|
403
413
|
_db_conn_path = None
|
|
404
414
|
|
|
@@ -410,6 +420,7 @@ def _get_owner_db_connection() -> Optional[sqlite3.Connection]:
|
|
|
410
420
|
db_conn.row_factory = sqlite3.Row
|
|
411
421
|
return db_conn
|
|
412
422
|
except (sqlite3.ProgrammingError, sqlite3.OperationalError):
|
|
423
|
+
unregister_connection(db_conn)
|
|
413
424
|
db_conn = None
|
|
414
425
|
_db_conn_path = None
|
|
415
426
|
|
|
@@ -425,11 +436,13 @@ def _get_owner_db_connection() -> Optional[sqlite3.Connection]:
|
|
|
425
436
|
try:
|
|
426
437
|
db_conn.execute("SELECT 1")
|
|
427
438
|
except Exception:
|
|
439
|
+
unregister_connection(db_conn)
|
|
428
440
|
try:
|
|
429
441
|
db_conn.close()
|
|
430
442
|
except Exception: # noqa: BLE001
|
|
431
443
|
pass
|
|
432
444
|
else:
|
|
445
|
+
unregister_connection(new_conn)
|
|
433
446
|
try:
|
|
434
447
|
new_conn.close()
|
|
435
448
|
except Exception: # noqa: BLE001
|
|
@@ -467,6 +480,11 @@ def _open_owner_db_connection(db_path: Path) -> Optional[sqlite3.Connection]:
|
|
|
467
480
|
)
|
|
468
481
|
except Exception as tuning_exc: # noqa: BLE001
|
|
469
482
|
logger.warning("Connection tuning skipped: %s", tuning_exc)
|
|
483
|
+
# Registered before migrations run, not after: a migration that returns
|
|
484
|
+
# without committing wedges every writer behind it exactly like any
|
|
485
|
+
# other ungated write, and startup is when the database is least
|
|
486
|
+
# observable.
|
|
487
|
+
register_connection(conn)
|
|
470
488
|
try:
|
|
471
489
|
from ..storage.db.migrations import (
|
|
472
490
|
DowngradeGuardError,
|
|
@@ -478,6 +496,7 @@ def _open_owner_db_connection(db_path: Path) -> Optional[sqlite3.Connection]:
|
|
|
478
496
|
except (MigrationError, DowngradeGuardError) as migration_exc:
|
|
479
497
|
# Shape migrations must fail loud — serving a half-migrated DB corrupts data.
|
|
480
498
|
logger.error("Schema migration failed; refusing to open database: %s", migration_exc)
|
|
499
|
+
unregister_connection(conn)
|
|
481
500
|
try:
|
|
482
501
|
conn.close()
|
|
483
502
|
except Exception: # noqa: BLE001
|
|
@@ -490,6 +509,7 @@ def _open_owner_db_connection(db_path: Path) -> Optional[sqlite3.Connection]:
|
|
|
490
509
|
"Unexpected schema migration failure; refusing to open database: %s",
|
|
491
510
|
migration_exc,
|
|
492
511
|
)
|
|
512
|
+
unregister_connection(conn)
|
|
493
513
|
try:
|
|
494
514
|
conn.close()
|
|
495
515
|
except Exception: # noqa: BLE001
|
|
@@ -511,6 +531,7 @@ def _open_owner_db_connection(db_path: Path) -> Optional[sqlite3.Connection]:
|
|
|
511
531
|
raise
|
|
512
532
|
logger.warning("Failed to create database connection: %s", e)
|
|
513
533
|
if conn is not None:
|
|
534
|
+
unregister_connection(conn)
|
|
514
535
|
try:
|
|
515
536
|
conn.close()
|
|
516
537
|
except Exception: # noqa: BLE001
|
|
@@ -240,6 +240,70 @@ def list_pending_derivation_retries(
|
|
|
240
240
|
return out
|
|
241
241
|
|
|
242
242
|
|
|
243
|
+
async def retry_single_derivation(
|
|
244
|
+
conn: sqlite3.Connection,
|
|
245
|
+
*,
|
|
246
|
+
source_id: str,
|
|
247
|
+
sync_batch_id: str,
|
|
248
|
+
job_name: str,
|
|
249
|
+
record_ids: Optional[Sequence[str]] = None,
|
|
250
|
+
) -> Dict[str, Any]:
|
|
251
|
+
"""Re-run exactly one recorded derivation debt.
|
|
252
|
+
|
|
253
|
+
Reloads the debt's canonical records and re-runs ONLY the job that failed,
|
|
254
|
+
so a retry cannot disturb jobs that already succeeded. Jobs are idempotent
|
|
255
|
+
per (batch, job) at the ledger level; a retry that fails again simply leaves
|
|
256
|
+
the debt in place for the next attempt.
|
|
257
|
+
|
|
258
|
+
Returns ``{"outcome": "recovered" | "still_failing" | "skipped", ...}``.
|
|
259
|
+
"skipped" means the debt cannot be re-run mechanically (unknown source, no
|
|
260
|
+
surviving records) — the caller decides whether that discharges it.
|
|
261
|
+
"""
|
|
262
|
+
if not job_name or not source_id:
|
|
263
|
+
return {"outcome": "skipped", "reason": "incomplete record"}
|
|
264
|
+
try:
|
|
265
|
+
from ..ingestion.canonical_pipeline import load_canonical_records_for_signal
|
|
266
|
+
# Reuses reprocess's resolver so a runtime-installed source rehydrates
|
|
267
|
+
# the same way it does for a manual reprocess.
|
|
268
|
+
from ..ingestion.reprocess import _resolve_source_def
|
|
269
|
+
|
|
270
|
+
try:
|
|
271
|
+
source_def = _resolve_source_def(source_id)
|
|
272
|
+
except ValueError:
|
|
273
|
+
return {"outcome": "skipped", "reason": "unknown source"}
|
|
274
|
+
records = load_canonical_records_for_signal(conn, source_def)
|
|
275
|
+
wanted = {str(r) for r in (record_ids or []) if r}
|
|
276
|
+
if wanted:
|
|
277
|
+
scoped = [
|
|
278
|
+
r
|
|
279
|
+
for r in records
|
|
280
|
+
if str(r.get("message_id") or r.get("record_id") or "") in wanted
|
|
281
|
+
]
|
|
282
|
+
records = scoped or records
|
|
283
|
+
if not records:
|
|
284
|
+
return {"outcome": "skipped", "reason": "no records"}
|
|
285
|
+
|
|
286
|
+
from .orchestrator import SignalDerivationOrchestrator
|
|
287
|
+
|
|
288
|
+
result = await SignalDerivationOrchestrator().run_signal_derivation(
|
|
289
|
+
records, source_id, job_names=[job_name], sync_batch_id=sync_batch_id
|
|
290
|
+
)
|
|
291
|
+
if result.get("errors"):
|
|
292
|
+
return {
|
|
293
|
+
"outcome": "still_failing",
|
|
294
|
+
"error": result["errors"][0].get("error"),
|
|
295
|
+
}
|
|
296
|
+
# run_signal_derivation clears the debt itself on success.
|
|
297
|
+
return {
|
|
298
|
+
"outcome": "recovered",
|
|
299
|
+
"records": len(records),
|
|
300
|
+
"created": result.get("records_created", {}).get(job_name, 0),
|
|
301
|
+
}
|
|
302
|
+
except Exception as exc: # noqa: BLE001 — one bad debt must not stop the rest
|
|
303
|
+
logger.warning("[DERIVE:RETRY] retry failed job=%s source=%s: %s", job_name, source_id, exc)
|
|
304
|
+
return {"outcome": "still_failing", "error": str(exc)}
|
|
305
|
+
|
|
306
|
+
|
|
243
307
|
async def retry_pending_derivations(
|
|
244
308
|
conn: Optional[sqlite3.Connection] = None,
|
|
245
309
|
*,
|
|
@@ -249,11 +313,6 @@ async def retry_pending_derivations(
|
|
|
249
313
|
) -> Dict[str, Any]:
|
|
250
314
|
"""Re-run derivation jobs that previously failed, clearing settled debts.
|
|
251
315
|
|
|
252
|
-
Reloads each debt's canonical records and re-runs ONLY the job that failed,
|
|
253
|
-
so a retry cannot disturb jobs that already succeeded. Jobs are idempotent
|
|
254
|
-
per (batch, job) at the ledger level; a retry that fails again simply leaves
|
|
255
|
-
the debt in place for the next attempt.
|
|
256
|
-
|
|
257
316
|
``dry_run`` reports what would run without touching anything.
|
|
258
317
|
"""
|
|
259
318
|
from ..core.state import get_db_connection
|
|
@@ -278,61 +337,27 @@ async def retry_pending_derivations(
|
|
|
278
337
|
]
|
|
279
338
|
return outcome
|
|
280
339
|
|
|
281
|
-
from ..ingestion.canonical_pipeline import load_canonical_records_for_signal
|
|
282
|
-
# Reuses reprocess's resolver so a runtime-installed source rehydrates the
|
|
283
|
-
# same way it does for a manual reprocess.
|
|
284
|
-
from ..ingestion.reprocess import _resolve_source_def
|
|
285
|
-
|
|
286
340
|
for debt in pending:
|
|
287
341
|
job_name = debt["job_name"]
|
|
288
342
|
src = debt["source_id"]
|
|
289
343
|
batch = debt["sync_batch_id"]
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
wanted = set(debt.get("record_ids") or [])
|
|
301
|
-
if wanted:
|
|
302
|
-
scoped = [
|
|
303
|
-
r
|
|
304
|
-
for r in records
|
|
305
|
-
if str(r.get("message_id") or r.get("record_id") or "") in wanted
|
|
306
|
-
]
|
|
307
|
-
records = scoped or records
|
|
308
|
-
if not records:
|
|
309
|
-
outcome["skipped"].append({"job_name": job_name, "source_id": src, "reason": "no records"})
|
|
310
|
-
continue
|
|
311
|
-
|
|
312
|
-
from .orchestrator import SignalDerivationOrchestrator
|
|
313
|
-
|
|
344
|
+
result = await retry_single_derivation(
|
|
345
|
+
conn,
|
|
346
|
+
source_id=src,
|
|
347
|
+
sync_batch_id=batch,
|
|
348
|
+
job_name=job_name,
|
|
349
|
+
record_ids=debt.get("record_ids") or [],
|
|
350
|
+
)
|
|
351
|
+
kind = result.pop("outcome")
|
|
352
|
+
entry = {"job_name": job_name, "source_id": src, **result}
|
|
353
|
+
if kind == "recovered":
|
|
314
354
|
outcome["attempted"] += 1
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
)
|
|
322
|
-
else:
|
|
323
|
-
# run_signal_derivation clears the debt itself on success.
|
|
324
|
-
outcome["recovered"].append(
|
|
325
|
-
{
|
|
326
|
-
"job_name": job_name,
|
|
327
|
-
"source_id": src,
|
|
328
|
-
"batch": batch,
|
|
329
|
-
"records": len(records),
|
|
330
|
-
"created": result.get("records_created", {}).get(job_name, 0),
|
|
331
|
-
}
|
|
332
|
-
)
|
|
333
|
-
except Exception as exc: # noqa: BLE001 — one bad debt must not stop the rest
|
|
334
|
-
logger.warning("[DERIVE:RETRY] retry failed job=%s source=%s: %s", job_name, src, exc)
|
|
335
|
-
outcome["still_failing"].append({"job_name": job_name, "source_id": src, "error": str(exc)})
|
|
355
|
+
outcome["recovered"].append({**entry, "batch": batch})
|
|
356
|
+
elif kind == "still_failing":
|
|
357
|
+
outcome["attempted"] += 1
|
|
358
|
+
outcome["still_failing"].append(entry)
|
|
359
|
+
else:
|
|
360
|
+
outcome["skipped"].append(entry)
|
|
336
361
|
|
|
337
362
|
outcome["pending_after"] = len(list_pending_derivation_retries(conn, source_id=source_id, limit=1000))
|
|
338
363
|
logger.info(
|
|
@@ -344,6 +369,67 @@ async def retry_pending_derivations(
|
|
|
344
369
|
return outcome
|
|
345
370
|
|
|
346
371
|
|
|
372
|
+
#: How long a worker-claimed retry waits for an active derivation batch to end
|
|
373
|
+
#: before handing the job back to the queue. Debts are recorded MID-batch, and
|
|
374
|
+
#: the worker claims within its poll interval — re-running immediately would
|
|
375
|
+
#: re-contend for the same write gate that produced the original "database is
|
|
376
|
+
#: locked". The wait doubles as the requeue backoff: while a long batch runs,
|
|
377
|
+
#: the worker cycles claim → wait → requeue at this period instead of spinning.
|
|
378
|
+
_IN_FLIGHT_WAIT_SECONDS = 30.0
|
|
379
|
+
_IN_FLIGHT_POLL_SECONDS = 0.5
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
async def run_derivation_retry_job(payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
383
|
+
"""Worker executor for ``SIGNAL_DERIVE_RETRY_KIND`` pipeline jobs.
|
|
384
|
+
|
|
385
|
+
This is the "by the worker" half of the module contract: the debt record IS
|
|
386
|
+
the queue row, so the worker claims it and re-runs the one derivation it
|
|
387
|
+
describes. Success marks the row done (the orchestrator clears the debt,
|
|
388
|
+
then the worker completes the job). A retry that fails again returns an
|
|
389
|
+
error status, parking the row 'failed' — still visible in every debt
|
|
390
|
+
listing, re-queued by the next organic failure of the same (batch, job), and
|
|
391
|
+
re-runnable via ``POST /signal/derivation-debt/retry``. One claim is one
|
|
392
|
+
attempt; nothing here loops.
|
|
393
|
+
"""
|
|
394
|
+
import asyncio
|
|
395
|
+
|
|
396
|
+
from .pipeline_activity import is_derivation_in_flight
|
|
397
|
+
|
|
398
|
+
waited = 0.0
|
|
399
|
+
while is_derivation_in_flight():
|
|
400
|
+
if waited >= _IN_FLIGHT_WAIT_SECONDS:
|
|
401
|
+
return {"status": "requeue", "reason": "derivation in flight"}
|
|
402
|
+
await asyncio.sleep(_IN_FLIGHT_POLL_SECONDS)
|
|
403
|
+
waited += _IN_FLIGHT_POLL_SECONDS
|
|
404
|
+
|
|
405
|
+
from ..core.state import get_db_connection
|
|
406
|
+
|
|
407
|
+
conn = get_db_connection()
|
|
408
|
+
if conn is None:
|
|
409
|
+
return {"status": "error", "error": "no database connection"}
|
|
410
|
+
|
|
411
|
+
job_name = str(payload.get("job_name") or "")
|
|
412
|
+
result = await retry_single_derivation(
|
|
413
|
+
conn,
|
|
414
|
+
source_id=str(payload.get("source_id") or ""),
|
|
415
|
+
sync_batch_id=str(payload.get("sync_batch_id") or "unknown"),
|
|
416
|
+
job_name=job_name,
|
|
417
|
+
record_ids=payload.get("record_ids") or [],
|
|
418
|
+
)
|
|
419
|
+
outcome = result.pop("outcome")
|
|
420
|
+
if outcome == "recovered":
|
|
421
|
+
return {
|
|
422
|
+
"status": "ok",
|
|
423
|
+
"records_created": {job_name: result.get("created", 0)},
|
|
424
|
+
**result,
|
|
425
|
+
}
|
|
426
|
+
if outcome == "skipped":
|
|
427
|
+
# A debt that cannot be re-run is still recorded data loss. Failing the
|
|
428
|
+
# job keeps it visible for a human instead of silently discharging it.
|
|
429
|
+
return {"status": "error", "error": f"cannot retry: {result.get('reason')}"}
|
|
430
|
+
return {"status": "error", "error": str(result.get("error") or "retry failed")}
|
|
431
|
+
|
|
432
|
+
|
|
347
433
|
def pending_derivation_summary(conn: Optional[sqlite3.Connection]) -> Dict[str, Any]:
|
|
348
434
|
"""What derived data this node KNOWS it is missing, and since when it knew.
|
|
349
435
|
|
{topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/attention_triage_job.py
RENAMED
|
@@ -7,6 +7,7 @@ unchanged, so re-running a day is safe and cheap.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import asyncio
|
|
10
11
|
import logging
|
|
11
12
|
from typing import Any, Callable, Dict, List, Optional
|
|
12
13
|
|
|
@@ -34,8 +35,7 @@ class AttentionTriageJob(BaseEnrichmentJob):
|
|
|
34
35
|
canonical_messages: List[Dict[str, Any]],
|
|
35
36
|
progress_callback: Optional[Callable[[int, int], None]] = None,
|
|
36
37
|
) -> List[Dict[str, Any]]:
|
|
37
|
-
|
|
38
|
-
if conn is None:
|
|
38
|
+
if get_db_connection() is None:
|
|
39
39
|
return [{"_deferred": True, "error": "database_unavailable"}]
|
|
40
40
|
days = set()
|
|
41
41
|
for row in canonical_messages:
|
|
@@ -46,17 +46,37 @@ class AttentionTriageJob(BaseEnrichmentJob):
|
|
|
46
46
|
break
|
|
47
47
|
done = 0
|
|
48
48
|
for day_iso in sorted(days):
|
|
49
|
+
# A day's re-triage holds the write gate — a blocking OS lock — for
|
|
50
|
+
# its verdict upserts and signal objects. Held on the event loop it
|
|
51
|
+
# stalls every coroutine, including the control-plane keepalive, so
|
|
52
|
+
# each day runs on a worker thread against that thread's own
|
|
53
|
+
# connection. One day per hop also keeps progress incremental.
|
|
54
|
+
def _triage(_day: str = day_iso) -> Optional[Dict[str, Any]]:
|
|
55
|
+
conn = get_db_connection()
|
|
56
|
+
if conn is None:
|
|
57
|
+
return None
|
|
58
|
+
return run_daily_triage(conn, _day)
|
|
59
|
+
|
|
49
60
|
try:
|
|
50
|
-
summary =
|
|
51
|
-
|
|
61
|
+
summary = await asyncio.to_thread(_triage)
|
|
62
|
+
if summary is not None:
|
|
63
|
+
logger.info("attention_triage %s: %s", day_iso, summary.get("quadrants"))
|
|
52
64
|
except Exception: # one bad day must not sink the batch
|
|
53
65
|
logger.exception("attention_triage failed for %s", day_iso)
|
|
54
66
|
done += 1
|
|
55
67
|
if progress_callback:
|
|
56
68
|
progress_callback(done, len(days))
|
|
57
|
-
|
|
69
|
+
|
|
70
|
+
def _award() -> Optional[List[Any]]:
|
|
71
|
+
conn = get_db_connection()
|
|
72
|
+
if conn is None:
|
|
73
|
+
return None
|
|
58
74
|
from ....features.triage.badges import award_badges
|
|
59
|
-
|
|
75
|
+
|
|
76
|
+
return award_badges(conn)
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
fresh = await asyncio.to_thread(_award)
|
|
60
80
|
if fresh:
|
|
61
81
|
logger.info("badges awarded: %s", fresh)
|
|
62
82
|
except Exception:
|
{topos_node-1.3.7 → topos_node-1.3.9}/topos/enrichment/jobs/canonical/dimension_summary_job.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import logging
|
|
4
5
|
from typing import Any, Callable, Dict, List, Optional
|
|
5
6
|
|
|
@@ -120,26 +121,37 @@ class DimensionSummaryJob(BaseEnrichmentJob):
|
|
|
120
121
|
|
|
121
122
|
structured_result: Dict[str, Any] = {}
|
|
122
123
|
if structured_signal_enabled():
|
|
123
|
-
structured_result = enrich_structured_batch(
|
|
124
|
-
prepared,
|
|
125
|
-
source_id=str(source_id or "") or None,
|
|
126
|
-
only_dimension=only_dimension,
|
|
127
|
-
)
|
|
128
|
-
# Piggyback (badge-pass pattern): auto-tag new conversations as
|
|
129
|
-
# work/personal with the configured local model. Best-effort;
|
|
130
|
-
# owner-set tags are never touched.
|
|
131
|
-
try:
|
|
132
|
-
from ....features.signal.conversation_context import (
|
|
133
|
-
classify_untagged_conversations,
|
|
134
|
-
)
|
|
135
124
|
|
|
136
|
-
|
|
137
|
-
|
|
125
|
+
def _structured() -> Dict[str, Any]:
|
|
126
|
+
# Artifact routing, the gate aggregates and the scope
|
|
127
|
+
# materializer each take the write gate — a blocking OS lock
|
|
128
|
+
# that on the event loop stalls every coroutine, including the
|
|
129
|
+
# control-plane keepalive. enrich_structured_batch resolves its
|
|
130
|
+
# own connection, so on a worker thread it binds that thread's.
|
|
131
|
+
out = enrich_structured_batch(
|
|
132
|
+
prepared,
|
|
133
|
+
source_id=str(source_id or "") or None,
|
|
134
|
+
only_dimension=only_dimension,
|
|
138
135
|
)
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
# Piggyback (badge-pass pattern): auto-tag new conversations as
|
|
137
|
+
# work/personal with the configured local model. Best-effort;
|
|
138
|
+
# owner-set tags are never touched.
|
|
139
|
+
try:
|
|
140
|
+
from ....features.signal.conversation_context import (
|
|
141
|
+
classify_untagged_conversations,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
worker_conn = get_db_connection()
|
|
145
|
+
if worker_conn is not None:
|
|
146
|
+
out["conversation_context"] = classify_untagged_conversations(
|
|
147
|
+
worker_conn
|
|
148
|
+
)
|
|
149
|
+
except Exception as exc: # noqa: BLE001 — never blocks enrichment
|
|
150
|
+
logger.debug("conversation-context pass failed: %s", exc)
|
|
151
|
+
return out
|
|
152
|
+
|
|
153
|
+
structured_result = await asyncio.to_thread(_structured)
|
|
141
154
|
|
|
142
|
-
store = DimensionBriefStore(conn)
|
|
143
155
|
updated_dims: List[str] = []
|
|
144
156
|
|
|
145
157
|
for idx, dimension in enumerate(dimensions):
|
|
@@ -199,13 +211,34 @@ class DimensionSummaryJob(BaseEnrichmentJob):
|
|
|
199
211
|
continue
|
|
200
212
|
if not any(str(v or "").strip() for v in section_updates.values()):
|
|
201
213
|
continue
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
214
|
+
|
|
215
|
+
def _merge(
|
|
216
|
+
_dimension: str = dimension,
|
|
217
|
+
_updates: Dict[str, Optional[str]] = section_updates,
|
|
218
|
+
_model: Optional[str] = (
|
|
219
|
+
result.output.get("model")
|
|
220
|
+
if result.status == "completed" and result.output
|
|
221
|
+
else None
|
|
222
|
+
),
|
|
223
|
+
_provider: str = provider,
|
|
224
|
+
) -> None:
|
|
225
|
+
# merge_system_update ensures the brief head and appends a
|
|
226
|
+
# revision, both under the write gate — a blocking OS lock that
|
|
227
|
+
# on the event loop stalls every coroutine, including the
|
|
228
|
+
# control-plane keepalive. The store binds whichever connection
|
|
229
|
+
# it is built with, so build it on the worker thread.
|
|
230
|
+
worker_conn = get_db_connection()
|
|
231
|
+
if worker_conn is None:
|
|
232
|
+
return
|
|
233
|
+
DimensionBriefStore(worker_conn).merge_system_update(
|
|
234
|
+
_dimension,
|
|
235
|
+
_updates,
|
|
236
|
+
source_id=source_id,
|
|
237
|
+
model=_model,
|
|
238
|
+
provider=_provider,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
await asyncio.to_thread(_merge)
|
|
209
242
|
updated_dims.append(dimension)
|
|
210
243
|
if progress_callback:
|
|
211
244
|
progress_callback(idx + 1, len(dimensions))
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
5
6
|
from typing import Any, Callable, Dict, List, Optional
|
|
@@ -169,7 +170,14 @@ class EntitiesJob(BaseEnrichmentJob):
|
|
|
169
170
|
|
|
170
171
|
if entity_spine_enabled() and results:
|
|
171
172
|
try:
|
|
172
|
-
|
|
173
|
+
# Spine resolution upserts entities, mentions, edges and
|
|
174
|
+
# dossiers under the write gate — a blocking OS lock that on
|
|
175
|
+
# the event loop stalls every coroutine, including the
|
|
176
|
+
# control-plane keepalive. It resolves its own connection, so
|
|
177
|
+
# on a worker thread it binds that thread's handle.
|
|
178
|
+
await asyncio.to_thread(
|
|
179
|
+
self._resolve_into_spine, results, canonical_messages
|
|
180
|
+
)
|
|
173
181
|
except Exception as exc:
|
|
174
182
|
logger.warning("entity spine resolution failed: %s", exc)
|
|
175
183
|
|