topos-node 1.3.39__tar.gz → 1.3.41__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.39/topos_node.egg-info → topos_node-1.3.41}/PKG-INFO +1 -1
- {topos_node-1.3.39 → topos_node-1.3.41}/pyproject.toml +1 -1
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/__version__.py +2 -2
- topos_node-1.3.41/topos/api/ingestion_api.py +155 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/__init__.py +1 -0
- topos_node-1.3.41/topos/core/handlers/graph_summary.py +76 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/ingest.py +72 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/disclosure/privacy_layer.py +52 -7
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/entities_job.py +14 -1
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/orchestrator.py +35 -5
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/projection.py +9 -1
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/edges.py +12 -2
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/graph_refresh.py +55 -0
- topos_node-1.3.41/topos/features/signal/conversation_windows.py +263 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/canonical_pipeline.py +73 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/ingest_helpers.py +16 -1
- topos_node-1.3.41/topos/ingestion/local_exports.py +713 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/manager.py +180 -6
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/chatgpt_export.py +46 -1
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/triggers/file_trigger.py +23 -2
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/pipeline/job_runner.py +43 -1
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/protocol/handled_message_types.json +3 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/retrieval.py +16 -1
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/raw/file_store.py +21 -1
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/upgrades/manifests.json +27 -0
- {topos_node-1.3.39 → topos_node-1.3.41/topos_node.egg-info}/PKG-INFO +1 -1
- {topos_node-1.3.39 → topos_node-1.3.41}/topos_node.egg-info/SOURCES.txt +3 -0
- topos_node-1.3.39/topos/api/ingestion_api.py +0 -80
- {topos_node-1.3.39 → topos_node-1.3.41}/LICENSE +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/MANIFEST.in +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/README.md +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/setup.cfg +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/shared/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/shared/filtering.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/shared/schema_registry.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_active_database_binding.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_active_ingestion_binding.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_extensions.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_gate_leg_hermeticity.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_llm_integrations_storage.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_local_gate_composition.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_owner_database_hermeticity.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_prewarm_first_run.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_profile_hygiene.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_profiles.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_protocol_handled_types.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_query_field_contract.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/tests/test_system_computer_name.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/dataset_resolution.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/duckdb_adapter.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/luck_surface.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/messenger_communities.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/messenger_directed.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/messenger_graph.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/messenger_labels.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/person_graph.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/person_overlay.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/profiles.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/query_engine.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/raw_queries.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/analytics/relationship_reads.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/analytics.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/app_registry.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/backup.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/community_naming_config.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/compute_remote.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/connected_apps.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/conversation_context.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/data_commit.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/data_explorer_table_prefs.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/database_explorer.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/db.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/device.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/disk_space_config.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/engine_run.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/enrichment.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/enrichment_lab.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/facts_llm_config.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/filter_lab.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/health.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/home_chat.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/ingestion_compat.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/ingestion_sources.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/intelligence_lifecycle.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/llm.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/local_mcp.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/messenger_analytics.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/privacy_disclose.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/query_api.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/runtime_bootstrap.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/sanitization_ollama_config.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/shell.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/signal.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/signal_extraction_config.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/source_install.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/source_scrub.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/sources.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/sources_overview.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/sync.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/tool_index.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/ui_config.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/uma_data.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/upgrades.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/usage.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/api/user_identity.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/app.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/auth.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/declared_field_map.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/mappers/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/mappers/base.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/mappers/browser_activity_mapper.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/mappers/chatgpt_mapper.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/mappers/demo_mappers.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/mappers/documents_mapper.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/mappers/github_activity_mapper.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/mappers/google_calendar_mapper.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/mappers/grok_mapper.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/mappers/messenger_mapper.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/models.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/canonicalization/resolver.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/cli/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/cli/__main__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/cli/assets/topos_blk_rounded.png +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/cli/assets/topos_white.png +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/cli/commands.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/cli/profile_cmd.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/cli/reprocess_cmd.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/cli/setup_models_cmd.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/cli/tray.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/config/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/config/conversation_context_llm.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/config/facts_llm.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/config/local_model_builds.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/config/model_packs.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/config/node_function_providers.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/config/sanitization_ollama.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/config/settings.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/config/signal_extraction.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/config/uvicorn_logging.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/contacts/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/contacts/identity.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/control_plane_client.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/api_models.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/connection_resilience.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/db_health.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/device_helpers.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/errors.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/events.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/common.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/complexity.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/config.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/contacts_import.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/database_explorer.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/derivation.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/device.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/enrichment.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/enrichment_lab.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/filter_lab.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/graph_query.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/home_chat.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/mcp_client_elevations.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/mcp_clients.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/messages.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/messenger_analytics.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/query.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/registry.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/routines.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/signal_features.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/sources.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/timeline_daily.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/tool_index.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/handlers/uma.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/logging.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/metrics.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/routine_access.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/startup_banner.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/state.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/table_layers.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/core/types.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/data_explorer_row_delete.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/data_explorer_table_prefs.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/data_explorer_tables.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/defaults.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/disclosure/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/disclosure/canonical_writer.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/disclosure/content_policy.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/disclosure/field_registry.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/disclosure/tier.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/backends/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/backends/base.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/backends/generative_prompts.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/backends/generative_response.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/backends/huggingface.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/backends/ollama.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/backends/openai.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/backends/openai_compatible.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/backends/redpill.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/backends/stub.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/client.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/disk_space.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/engine.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/hosted_llm_wallet.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/intake.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/memory_utils.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/model_cache.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/model_manager.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/ollama_install.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/ollama_pull.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/ollama_runtime.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/ollama_setup_guidance.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/pipeline_memory.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/queue_manager.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/registration.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/result_formatter.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/router.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/scoped_token.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/tasks.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/torch_runtime.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/transport.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/usage_guard.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/usage_observation.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/engine/validator.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/catalog.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/derivation_recovery.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/derived_tables.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/job_readiness.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/job_writer.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/base.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/_batch_limits.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/_engine_runner.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/attention_triage_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/availability_scores_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/brief_fallback.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/complexity_snapshot_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/derivation_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/derived_object_embeddings_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/dimension_summary_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/embeddings_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/emo_27_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/fact_extraction_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/goal_extraction_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/pii_redaction_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/relationship_edges_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/sentiment_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/statistics_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/timeline_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/topic_clusters_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/canonical/topics_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/raw/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/raw/attachments_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/raw/language_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/raw/time_normalization_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/jobs/raw/tool_calls_job.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/model_overrides.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/models/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/models/manager.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/models/mvp_defaults.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/models/registry.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/models/versioning.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/pipeline_activity.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/processor.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/progress_bar.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/record_progress.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/registry_contract.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/signal_derivation_config.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment/source_overrides.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment_lab/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment_lab/bundles.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment_lab/model_resolve.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment_lab/schema.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment_lab/service.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment_lab/store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/enrichment_lab/worker.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/evals/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/evals/enrichment_eval.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/extensions.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/canonical.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/engine.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/entropy.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/focus.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/graph_metrics.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/influence.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/snapshots.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/topic_merge.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/topics.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/topics_daily.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/complexity/volatility.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/_scales.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/admin.legal.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/aspirations.goals.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/behavior.habits.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/behavior.preferences.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/beliefs.civic.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/beliefs.spiritual.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/communication.style.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/community.membership.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/craft.tooling.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/expertise.knowledge.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/finances.economics.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/health.lifestyle.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/health.mental.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/health.physical.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/household.pets.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/identity.core.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/interests.taste.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/life.events.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/narrative.self.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/net.capability.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/obligations.commitments.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/personality.traits.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/places.mobility.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/possessions.assets.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/relationships.social.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/values.motivation.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/vocab/dispositions.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/vocab/expertise.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/vocab/habits.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/vocab/interests.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/vocab/preferences.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/vocab/stances.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/vocab/tools.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/vocab/values.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/bundled_packs/work.career.yaml +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/candidates.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/guard.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/kernels.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/net_subject_policy.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/packs.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/person_bridge.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/prefilter.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/registry.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/report_card.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/router.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/social_bench.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/social_kernels.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/surfaces.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/synthesize.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/template.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/verify.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/derivation/writer.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/affinity.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/affinity_owner.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/affinity_quality.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/chatgpt_declared.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/community_names.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/community_naming.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/consolidation.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/context_vectors.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/cypher.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/declared_mappings.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/dossier.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/fact_materializer.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/graph_enrichers.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/graph_search.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/linking.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/maintenance.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/owner.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/reads.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/rebuild_subprocess.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/resolver.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/entities/structured_fields.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/facts/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/facts/extract.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/facts/llm_extract.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/facts/reads.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/facts/store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/facts/verdicts.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/fit/evaluator.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/fit/opportunity_types.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/lifecycle/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/lifecycle/blackhole.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/lifecycle/blackhole_guard.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/lifecycle/blackhole_llm.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/lifecycle/blackhole_rebuild.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/lifecycle/derived_scrub.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/lifecycle/exclusions.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/lifecycle/gc.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/provenance/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/provenance/posture.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/provenance/roles.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/brief_canonical_loader.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/brief_ontology.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/brief_renderer.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/brief_schemas.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/chunking.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/cluster_labels.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/conversation_context.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/data_health.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/brief_ontologies.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/intentions.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/interests.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/memory.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/places.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/profile.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/relationships.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/resources.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/schema/dimension_definition.schema.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/time.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/wellbeing.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/definitions/work.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/derived_index.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/derived_spec.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/dimension_briefs.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/dimension_definition_loader.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/dimension_profiles.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/dimension_registry.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/embed_context.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/extraction/artifact_router.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/extraction/artifact_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/extraction/rule_extractors.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/graph_sanitize.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/hybrid_search.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/incremental_clustering.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/model_recommendations.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/query_embed_cache.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/schemas.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/service.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/signal_object_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/source_hydration.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/structured_signal.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/tool_index.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/topic_clustering.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/typed_stores/aggregates.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/typed_stores/commitments.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/typed_stores/rhythm.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/typed_stores/scope_materializer.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/vector_codec.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/vector_math.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/signal/vector_settings.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/stats/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/stats/definitions.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/stats/engine.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/stats/fold.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/stats/insights.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/timeline_projection.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/timeline_rollup.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/triage/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/triage/badges.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/triage/daily.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/triage/dashboard.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/triage/instruments.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/triage/intents.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/features/triage/readiness.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/filter_lab/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/filter_lab/bundles.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/filter_lab/schema.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/filter_lab/service.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/filter_lab/store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/filter_lab/worker.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/home_chat/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/home_chat/schema.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/home_chat/store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/hosted_pool_lease.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/checkpoints/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/checkpoints/checkpoint_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/checkpoints/sqlite_checkpoint_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/inbox_crypto.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/inbox_drain.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/jobs.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/journal_location_fanout.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/journal_time_log_normalize.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/local_sync.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/log_preview.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parser.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/base.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/browser_parser.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/calendar_parser.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/chatgpt_conversation_flattener.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/chatgpt_parser.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/demo_file_parsers.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/documents_parser.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/github_activity_parser.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/google_calendar_parser.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/grok_parser.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/messenger_parser.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/parsers/voxterm_parser.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/progress.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/reprocess.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/sources/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/sources/base.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/sources/calendar.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/sources/chatgpt.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/sources/contact_importers.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/sources/grok.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/sources/imessage_reader.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/sources/signal_export_parser.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/sources/signal_reader.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/state_machine.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/triggers/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/triggers/sqlite_trigger.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/usage_inbox_dedupe.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/validation/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/validation/base.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/validation/schema_registry.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/ingestion/validation/schema_validator.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/lineage/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/lineage/provenance.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/lineage/tracker.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/llm_integrations_storage.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/mcp_clients.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/mcp_stdio_proxy.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/observability/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/observability/alerts.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/observability/metrics.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/observability/tracing.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/openai_client.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/owner_key.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/pipeline/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/pipeline/audit.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/pipeline/envelope.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/pipeline/job_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/pipeline/stages.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/pipeline/stub_enqueue.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/principal.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/profiles.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/projections/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/projections/vector_index/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/projections/vector_index/base.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/projections/vector_index/builders.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/projections/vector_index/health_checks.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/protocol/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/protocol/narrowing_vocabulary.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/protocol/query_field_contract.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/audit.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/closeness.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/cohort_resolvers.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/collaborators.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/ddr.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/disclosure.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/entity_window.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/exclusion.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/facts_direct.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/fingerprint.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/game_layer.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/grant_cache.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/inference.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/intent.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/manifest.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/manifest_validation.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/minimizer.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/narrowing.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/negotiation.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/packet_resolution.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/pipeline.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/planner.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/runtime.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/scope_arbiter.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/scope_classifier.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/scope_head.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/scope_registry.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/scope_registry_loader.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/scope_shadow.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/session.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/session_utils.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/source_generation.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/truth_facts.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/truth_prompts.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/turn_classifier.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/types.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/verify.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/query/verify_modes.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/rate_limit.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/relay_stamp.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/routines/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/routines/schema.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/routines/store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/runtime_housekeeping.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/runtime_shutdown.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/runtime_update.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sanitization/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sanitization/nsfw_classifier.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sanitization/ollama_transforms.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sanitization/prewarm.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sanitization/privacy_filter.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/scope_resolution.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/container.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/embeddings/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/embeddings/base.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/embeddings/local.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/embeddings/remote.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/interfaces.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/llm/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/llm/base.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/llm/openai.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/local.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/services/postgres.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sources/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sources/bundled_canonical_triples.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sources/canonical_signal_defaults.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sources/declared_field_map_spec.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sources/definitions.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sources/install_service.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sources/registry.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sources/runtime_install.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sources/scrub_attribution.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sources/scrub_service.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/startup_banner.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/adapters/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/adapters/factory.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/adapters/fakes.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/adapters/postgres/stores.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/adapters/protocols.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/adapters/sqlite/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/adapters/sqlite/stores.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/adapters/sqlite/vector_search.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/activity_tables.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/ai_chat/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/ai_chat/canonicalizer.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/ai_chat/mapper.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/ai_chat/model.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/ai_chat/tables.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/calendar_contacts_tables.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/canonical_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/conversations_tables.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/journal_repair.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/mapping_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/canonical/postgres.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/client.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/connection_tuning.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/activity_events_content_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/actor_role_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/affinity_pair_labels_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/attention_triage_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/attention_triage_v2.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/backup.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/backup_custody.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/calendar_events_scheduling_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/canonical_address_book_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/canonical_disclosure_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/canonical_nsfw_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/community_names_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/complexity_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/conflict_provenance_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/conversation_context_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/derivation_factory_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/derivation_ledger_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/derivation_provenance_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/derived_row_identity_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/documents_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/enrichment_record_progress_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/enrichment_spec_version_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/entity_affinity_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/entity_blackhole_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/entity_context_vectors_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/entity_context_vectors_v2.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/entity_edges_validity_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/entity_mentions_authored_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/entity_review_candidate_bar_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/entity_review_dedup_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/entity_review_provenance_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/episodes_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/extraction_artifacts.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/fact_dimension_by_entity_type_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/journal_entries_duration_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/journal_entries_ends_at_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/journal_entries_people_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/journal_entries_place_name_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/journal_entries_starts_at_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/journal_origin_dimension_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/mention_table_stamp_backfill_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/message_emotions_role_backfill_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/message_emotions_role_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/net_subject_policy_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/pack_registry_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/pipeline_jobs_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/query_artifacts_duration_ms_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/registry.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/registry_checksums.json +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/remediation_person_model.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/retire_url_classification_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/scope_source_generation.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/signal_dimension_backfill_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/signal_dimension_harness.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/signal_objects.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/signal_objects_period_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/signal_objects_updated_by_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/signal_summaries_drop_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/stage9_column_renames.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/vector_storage_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/vector_storage_v2.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/vector_storage_v3.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/vector_storage_v4.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_clusters_v2.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_entities_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_entity_review_v2.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_facts_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_lifecycle_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_mvp_phase0.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_mvp_phase1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_mvp_phase4_messages_cutover.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_mvp_phase5_topic_clusters.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_mvp_phase6_dimension_briefs.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_mvp_query_quality.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_mvp_topic_clusters_coordination.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_stats_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/migrations/wiki_timeline_v1.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/paths.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/postgres.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/schema.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/schema_probe.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/storage_breakdown.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/db/write_gate.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/derived_row_identity.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/enrichment/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/enrichment/canonical_enrichment_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/enrichment/raw_enrichment_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/normalized/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/normalized/normalized_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/oplog/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/oplog/decision.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/oplog/oplog_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/oplog/postgres.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/person/identity_resolver.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/projections/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/projections/index_ops_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/projections/vector_index_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/raw/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/raw/browser_flat_tables.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/raw/raw_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/raw/raw_tables_manager.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/raw/sqlite_raw_store.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/security/encryption.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/signal_identity.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/source_settings.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/storage/user_identity.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sync/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sync/client.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/sync_handlers.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/testing/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/testing/lifespan.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/uds.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/uma_contact_enrichment.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/uma_filters.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/uma_resource_id.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/uma_rpt.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/upgrades/__init__.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/upgrades/runner.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/utils/base_object.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos/websocket_client.py +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos_node.egg-info/dependency_links.txt +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos_node.egg-info/entry_points.txt +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/topos_node.egg-info/requires.txt +0 -0
- {topos_node-1.3.39 → topos_node-1.3.41}/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.41"
|
|
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"
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Body, Depends, HTTPException, Request
|
|
4
|
+
|
|
5
|
+
from ..auth import require_api_key
|
|
6
|
+
from ..ingestion.reprocess import reprocess_source
|
|
7
|
+
from .ingestion_sources import ingest_source
|
|
8
|
+
|
|
9
|
+
router = APIRouter()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@router.post("/ingestion/start")
|
|
13
|
+
async def start_ingestion() -> dict:
|
|
14
|
+
return {"status": "stub"}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@router.post("/ingestion/progress")
|
|
18
|
+
async def report_progress() -> dict:
|
|
19
|
+
return {"status": "stub"}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@router.post("/ingestion/reprocess", dependencies=[Depends(require_api_key)])
|
|
23
|
+
async def ingestion_reprocess(payload: dict = Body(default_factory=dict)) -> dict:
|
|
24
|
+
"""Re-run raw→canonical for a source (optional newest-N via ``limit``).
|
|
25
|
+
|
|
26
|
+
Body:
|
|
27
|
+
- source_id (required)
|
|
28
|
+
- dataset_id (required)
|
|
29
|
+
- from_stage: ``raw`` (default) | ``canonical``
|
|
30
|
+
- limit: optional int — newest N raw rows only
|
|
31
|
+
- run_enrichment: optional bool (default true)
|
|
32
|
+
- force / sync_batch_id: optional
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
limit_raw = payload.get("limit")
|
|
36
|
+
limit = int(limit_raw) if limit_raw is not None and str(limit_raw).strip() != "" else None
|
|
37
|
+
return await reprocess_source(
|
|
38
|
+
source_id=str(payload["source_id"]),
|
|
39
|
+
dataset_id=str(payload["dataset_id"]),
|
|
40
|
+
from_stage=payload.get("from_stage") or "raw",
|
|
41
|
+
sync_batch_id=payload.get("sync_batch_id"),
|
|
42
|
+
force=bool(payload.get("force", False)),
|
|
43
|
+
limit=limit,
|
|
44
|
+
run_enrichment=bool(payload.get("run_enrichment", True)),
|
|
45
|
+
)
|
|
46
|
+
except KeyError as exc:
|
|
47
|
+
raise HTTPException(status_code=400, detail=f"Missing field: {exc}") from exc
|
|
48
|
+
except ValueError as exc:
|
|
49
|
+
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@router.get("/ingestion/audit", dependencies=[Depends(require_api_key)])
|
|
53
|
+
async def get_ingestion_audit(sync_batch_id: str) -> dict:
|
|
54
|
+
from ..core.state import get_db_connection
|
|
55
|
+
from ..pipeline.audit import SQLiteIngestAuditStore
|
|
56
|
+
|
|
57
|
+
conn = get_db_connection()
|
|
58
|
+
if not conn:
|
|
59
|
+
raise HTTPException(status_code=503, detail="Database unavailable")
|
|
60
|
+
store = SQLiteIngestAuditStore(conn)
|
|
61
|
+
return {"sync_batch_id": sync_batch_id, "stages": store.query_by_batch(sync_batch_id)}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@router.post("/ingestion/upload", dependencies=[Depends(require_api_key)])
|
|
65
|
+
async def upload_ingestion_file(request: Request) -> dict:
|
|
66
|
+
return await ingest_source("chatgpt_file_ingestion", request)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@router.get("/ingestion/local-exports", dependencies=[Depends(require_api_key)])
|
|
70
|
+
async def list_local_exports() -> dict:
|
|
71
|
+
"""Exports sitting on this machine, for the import screen to offer.
|
|
72
|
+
|
|
73
|
+
The node is the only party that can answer this: a browser cannot read a
|
|
74
|
+
filesystem, and cannot produce an absolute path even for a file the user
|
|
75
|
+
picks. So the node looks, and hands back opaque handles plus labels a person
|
|
76
|
+
can recognise -- never the paths themselves.
|
|
77
|
+
|
|
78
|
+
Bounded and stateless by construction (see ``local_exports``): a few known
|
|
79
|
+
folders, shallow, symlinks skipped, time-boxed, nothing written down.
|
|
80
|
+
"""
|
|
81
|
+
import asyncio
|
|
82
|
+
|
|
83
|
+
from ..ingestion.local_exports import find_exports
|
|
84
|
+
|
|
85
|
+
result = await asyncio.to_thread(find_exports)
|
|
86
|
+
return result.as_payload()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@router.get("/ingestion/local-exports/{handle}/describe", dependencies=[Depends(require_api_key)])
|
|
90
|
+
async def describe_local_export(handle: str) -> dict:
|
|
91
|
+
"""What is inside one discovered export: conversation count and date range.
|
|
92
|
+
|
|
93
|
+
Separate from discovery on purpose. Listing candidates reads only an
|
|
94
|
+
archive's central directory and stays in the low hundreds of milliseconds;
|
|
95
|
+
this opens the conversations file and parses it, which is about a second on
|
|
96
|
+
a 52 MB export. Paying that for every row of a scan to inform a choice about
|
|
97
|
+
one of them would be the wrong trade.
|
|
98
|
+
|
|
99
|
+
It exists because a window is measured against today while an export is
|
|
100
|
+
fixed in the past, and nothing in the UI could see the gap: a six-month
|
|
101
|
+
window against a fourteen-month-old export drops every conversation and
|
|
102
|
+
completes successfully with nothing imported.
|
|
103
|
+
"""
|
|
104
|
+
import asyncio
|
|
105
|
+
|
|
106
|
+
from ..ingestion.local_exports import describe_export, resolve
|
|
107
|
+
|
|
108
|
+
path = await asyncio.to_thread(resolve, handle)
|
|
109
|
+
if path is None:
|
|
110
|
+
raise HTTPException(
|
|
111
|
+
status_code=404,
|
|
112
|
+
detail="That export is no longer where we found it. Scan again and pick it.",
|
|
113
|
+
)
|
|
114
|
+
try:
|
|
115
|
+
return await asyncio.to_thread(describe_export, path)
|
|
116
|
+
except Exception as exc: # noqa: BLE001 — a describe failure must not block the import
|
|
117
|
+
raise HTTPException(status_code=422, detail=f"Could not read that export: {exc}") from exc
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@router.post("/ingestion/upload-local-path", dependencies=[Depends(require_api_key)])
|
|
121
|
+
async def upload_ingestion_local_path(
|
|
122
|
+
request: Request, payload: dict = Body(default_factory=dict)
|
|
123
|
+
) -> dict:
|
|
124
|
+
"""Ingest a file that is already on this machine. No bytes cross a network.
|
|
125
|
+
|
|
126
|
+
Accepts either a ``handle`` from ``/ingestion/local-exports`` -- the path
|
|
127
|
+
never leaves the node -- or an explicit ``file_path``, which is still how
|
|
128
|
+
someone points at an export outside the searched folders.
|
|
129
|
+
"""
|
|
130
|
+
from ..ingestion.local_exports import resolve
|
|
131
|
+
|
|
132
|
+
dataset_id = payload.get("dataset_id")
|
|
133
|
+
file_path = payload.get("file_path")
|
|
134
|
+
handle = str(payload.get("handle") or "").strip()
|
|
135
|
+
|
|
136
|
+
if handle and not file_path:
|
|
137
|
+
resolved = resolve(handle)
|
|
138
|
+
if resolved is None:
|
|
139
|
+
# Handles are re-found rather than remembered, so a stale one means
|
|
140
|
+
# the file moved or went away -- not that the user did anything odd.
|
|
141
|
+
raise HTTPException(
|
|
142
|
+
status_code=404,
|
|
143
|
+
detail="That export is no longer where we found it. Scan again and pick it.",
|
|
144
|
+
)
|
|
145
|
+
file_path = str(resolved)
|
|
146
|
+
|
|
147
|
+
if not file_path:
|
|
148
|
+
raise HTTPException(status_code=400, detail="Provide a handle or a file_path.")
|
|
149
|
+
|
|
150
|
+
return await ingest_source(
|
|
151
|
+
"chatgpt_file_ingestion",
|
|
152
|
+
request,
|
|
153
|
+
dataset_id=dataset_id,
|
|
154
|
+
file_path=file_path,
|
|
155
|
+
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""How much is in the graph — the smallest possible answer.
|
|
2
|
+
|
|
3
|
+
Why this exists rather than a count taken from ``/v1/signal/graph``: that
|
|
4
|
+
endpoint returns node and edge LISTS, capped at 200 and 500, and no totals. To
|
|
5
|
+
learn "how many" from it you would fetch rows you throw away, and still not
|
|
6
|
+
know the answer once the graph passed the cap.
|
|
7
|
+
|
|
8
|
+
Why not carry the number on the ingestion job instead: the progress channel is
|
|
9
|
+
a fixed set of columns on the control plane's ``ingestion_jobs`` table, with no
|
|
10
|
+
JSON field, and ``stage_event`` is filtered down to four keys. Adding it there
|
|
11
|
+
means a schema migration on the production database — a real cost to pay for a
|
|
12
|
+
reassurance line, and a change that is hard to take back.
|
|
13
|
+
|
|
14
|
+
So: three COUNT(*) queries, read on demand, owned by nothing.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import asyncio
|
|
20
|
+
from typing import Any, Dict, Optional
|
|
21
|
+
|
|
22
|
+
from .registry import handles
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _counts() -> Dict[str, Any]:
|
|
26
|
+
from ...core.state import get_db_connection
|
|
27
|
+
|
|
28
|
+
conn = get_db_connection()
|
|
29
|
+
if conn is None:
|
|
30
|
+
return {"available": False}
|
|
31
|
+
|
|
32
|
+
out: Dict[str, Any] = {"available": True}
|
|
33
|
+
# Each table is counted independently and a failure on one is not a failure
|
|
34
|
+
# of the reply: a missing table on an older node should cost that number,
|
|
35
|
+
# not the whole answer.
|
|
36
|
+
for key, table in (
|
|
37
|
+
("nodes", "graph_nodes"),
|
|
38
|
+
("edges", "graph_edges"),
|
|
39
|
+
("entities", "entities"),
|
|
40
|
+
):
|
|
41
|
+
try:
|
|
42
|
+
row = conn.execute(f"SELECT COUNT(*) FROM {table}").fetchone() # noqa: S608 — fixed names
|
|
43
|
+
out[key] = int(row[0]) if row else 0
|
|
44
|
+
except Exception: # noqa: BLE001
|
|
45
|
+
out[key] = None
|
|
46
|
+
try:
|
|
47
|
+
# The column is `last_run_at` -- what _mark_materialized writes. This read
|
|
48
|
+
# `materialized_at`, which does not exist, and the per-field try/except
|
|
49
|
+
# turned that into a permanent null rather than an error: shipped in
|
|
50
|
+
# 1.3.40 and noticed only because the live reply carried the null.
|
|
51
|
+
row = conn.execute(
|
|
52
|
+
"SELECT MAX(last_run_at) FROM graph_materialization_state"
|
|
53
|
+
).fetchone()
|
|
54
|
+
out["materialized_at"] = row[0] if row else None
|
|
55
|
+
except Exception: # noqa: BLE001
|
|
56
|
+
out["materialized_at"] = None
|
|
57
|
+
return out
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@handles("graph_summary")
|
|
61
|
+
async def handle_graph_summary(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
62
|
+
"""Node, edge and entity counts, for a screen that wants to show growth.
|
|
63
|
+
|
|
64
|
+
The reason it is worth showing during an import: entity data lands at the
|
|
65
|
+
third of twenty stages and the graph is materialised twice, so these numbers
|
|
66
|
+
move long before the import finishes. Watching them move is the difference
|
|
67
|
+
between a long wait and a long wait you can see the point of.
|
|
68
|
+
"""
|
|
69
|
+
req_id = message.get("id")
|
|
70
|
+
if not req_id:
|
|
71
|
+
return None
|
|
72
|
+
try:
|
|
73
|
+
payload = await asyncio.to_thread(_counts)
|
|
74
|
+
return {"id": req_id, "status": "ok", "payload": payload}
|
|
75
|
+
except Exception as exc: # noqa: BLE001
|
|
76
|
+
return {"id": req_id, "status": "error", "error": str(exc)}
|
|
@@ -365,6 +365,22 @@ async def handle_start_ingestion(message: Dict[str, Any]) -> Optional[Dict[str,
|
|
|
365
365
|
file_url = payload.get("file_url")
|
|
366
366
|
file_base64 = payload.get("file_base64")
|
|
367
367
|
file_path = payload.get("file_path")
|
|
368
|
+
local_handle = str(payload.get("local_export_handle") or "").strip()
|
|
369
|
+
if local_handle and not file_path:
|
|
370
|
+
# A handle from `list_local_exports`. The path was never sent to the
|
|
371
|
+
# browser, so it is resolved here — and only ever to something still
|
|
372
|
+
# inside a searched folder, which is what makes a handle safe to accept
|
|
373
|
+
# back over the relay.
|
|
374
|
+
from ...ingestion.local_exports import resolve as _resolve_local_export
|
|
375
|
+
|
|
376
|
+
resolved = _resolve_local_export(local_handle)
|
|
377
|
+
if resolved is None:
|
|
378
|
+
return {
|
|
379
|
+
"id": req_id,
|
|
380
|
+
"status": "error",
|
|
381
|
+
"error": "That export is no longer where we found it. Scan again and pick it.",
|
|
382
|
+
}
|
|
383
|
+
file_path = str(resolved)
|
|
368
384
|
job_id = payload.get("job_id")
|
|
369
385
|
source_id = payload.get("source_id") # Optional source_id from control plane
|
|
370
386
|
source_definition = payload.get("source_definition")
|
|
@@ -481,6 +497,62 @@ async def handle_ingestion_reprocess(message: Dict[str, Any]) -> Optional[Dict[s
|
|
|
481
497
|
except Exception as exc: # noqa: BLE001
|
|
482
498
|
return {"id": req_id, "status": "error", "error": str(exc)}
|
|
483
499
|
|
|
500
|
+
@handles("list_local_exports")
|
|
501
|
+
async def handle_list_local_exports(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
502
|
+
"""Exports sitting on this machine, for the import screen to offer.
|
|
503
|
+
|
|
504
|
+
Only the node can answer this. A browser cannot read a filesystem, and
|
|
505
|
+
cannot produce an absolute path even for a file the user picks — so without
|
|
506
|
+
this the only way to reach the free import path was to type one out by hand.
|
|
507
|
+
|
|
508
|
+
What comes back is opaque handles and labels a person can recognise, never
|
|
509
|
+
paths: the reply travels to the browser over the relay, and a home-directory
|
|
510
|
+
path carries the account name and whatever else lives beside it.
|
|
511
|
+
"""
|
|
512
|
+
req_id = message.get("id")
|
|
513
|
+
if not req_id:
|
|
514
|
+
return None
|
|
515
|
+
try:
|
|
516
|
+
import asyncio
|
|
517
|
+
|
|
518
|
+
from ...ingestion.local_exports import find_exports
|
|
519
|
+
|
|
520
|
+
result = await asyncio.to_thread(find_exports)
|
|
521
|
+
return {"id": req_id, "status": "ok", "payload": result.as_payload()}
|
|
522
|
+
except Exception as exc: # noqa: BLE001
|
|
523
|
+
return {"id": req_id, "status": "error", "error": str(exc)}
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
@handles("describe_local_export")
|
|
527
|
+
async def handle_describe_local_export(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
528
|
+
"""Conversation count and date range for one discovered export.
|
|
529
|
+
|
|
530
|
+
The window a person picks is measured from today; the export is fixed in
|
|
531
|
+
the past. Nothing in the UI could see that gap, so a six-month window
|
|
532
|
+
against a fourteen-month-old file imported nothing and said it succeeded.
|
|
533
|
+
This is the number that closes it, read where the dates are.
|
|
534
|
+
"""
|
|
535
|
+
req_id = message.get("id")
|
|
536
|
+
if not req_id:
|
|
537
|
+
return None
|
|
538
|
+
payload = message.get("payload") or {}
|
|
539
|
+
handle = str(payload.get("handle") or "").strip()
|
|
540
|
+
if not handle:
|
|
541
|
+
return {"id": req_id, "status": "error", "error": "handle required"}
|
|
542
|
+
try:
|
|
543
|
+
import asyncio
|
|
544
|
+
|
|
545
|
+
from ...ingestion.local_exports import describe_export, resolve
|
|
546
|
+
|
|
547
|
+
path = await asyncio.to_thread(resolve, handle)
|
|
548
|
+
if path is None:
|
|
549
|
+
return {"id": req_id, "status": "error", "error": "export no longer found"}
|
|
550
|
+
described = await asyncio.to_thread(describe_export, path)
|
|
551
|
+
return {"id": req_id, "status": "ok", "payload": described}
|
|
552
|
+
except Exception as exc: # noqa: BLE001
|
|
553
|
+
return {"id": req_id, "status": "error", "error": str(exc)}
|
|
554
|
+
|
|
555
|
+
|
|
484
556
|
@handles("get_ingestion_audit")
|
|
485
557
|
async def handle_get_ingestion_audit(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
486
558
|
req_id = message.get("id")
|
|
@@ -169,13 +169,36 @@ class PrivacyLayerClient:
|
|
|
169
169
|
return {"status": "failed", "error": str(last_exc), "items": []}
|
|
170
170
|
|
|
171
171
|
|
|
172
|
+
# Names for the two model passes below. They are reported to the caller's
|
|
173
|
+
# progress callback and surface verbatim in the UI, so they are written for a
|
|
174
|
+
# person watching an import rather than for a log grep.
|
|
175
|
+
#
|
|
176
|
+
# "Filtering private information" rather than "checking for private details":
|
|
177
|
+
# this pass does not merely look. It detects PII spans and REDACTS them into
|
|
178
|
+
# the disclosure copy, which is the whole point of the layer — the node is
|
|
179
|
+
# preparing what it can safely show. A name that says "checking" describes an
|
|
180
|
+
# inspection and undersells the thing the product actually does for you.
|
|
181
|
+
PRIVACY_STAGE_REDACT = "filtering private information"
|
|
182
|
+
PRIVACY_STAGE_NSFW = "checking for sensitive content"
|
|
183
|
+
|
|
184
|
+
# How many records to send per call. NOT a throughput choice: both engine
|
|
185
|
+
# backends (redact_privacy_batch, classify_nsfw_batch) loop over `items` one at
|
|
186
|
+
# a time, so grouping 32 records buys no batched inference -- it is purely
|
|
187
|
+
# transport. The only thing the group size decides is how often progress can
|
|
188
|
+
# move. Measured on a live import: ~3.75s per record on CPU, so a group of 32
|
|
189
|
+
# meant the bar stepped once every two minutes and read as frozen; at 4 it
|
|
190
|
+
# moves about every fifteen seconds. The per-call overhead is a dict and a
|
|
191
|
+
# thread hop against ~15s of model work.
|
|
192
|
+
PRIVACY_PROGRESS_CHUNK = 4
|
|
193
|
+
|
|
194
|
+
|
|
172
195
|
async def run_privacy_disclosure_layer(
|
|
173
196
|
conn,
|
|
174
197
|
canonical_messages: List[Dict[str, Any]],
|
|
175
198
|
*,
|
|
176
199
|
source_group: Optional[str] = None,
|
|
177
200
|
client: Optional[PrivacyLayerClient] = None,
|
|
178
|
-
progress_callback: Optional[Callable[[int, int], None]] = None,
|
|
201
|
+
progress_callback: Optional[Callable[[int, int, str], None]] = None,
|
|
179
202
|
nsfw_only: bool = False,
|
|
180
203
|
) -> Dict[str, Any]:
|
|
181
204
|
"""Mandatory post-canonical Platform Privacy Layer: PII disclosure + NSFW tags.
|
|
@@ -242,7 +265,7 @@ async def run_privacy_disclosure_layer(
|
|
|
242
265
|
table = canonical_table_for_message(msg, source_group=source_group)
|
|
243
266
|
if not table:
|
|
244
267
|
if progress_callback:
|
|
245
|
-
progress_callback(idx + 1, total)
|
|
268
|
+
progress_callback(idx + 1, total, PRIVACY_STAGE_REDACT)
|
|
246
269
|
continue
|
|
247
270
|
record_id = (
|
|
248
271
|
msg.get("message_id")
|
|
@@ -252,7 +275,7 @@ async def run_privacy_disclosure_layer(
|
|
|
252
275
|
)
|
|
253
276
|
if not record_id:
|
|
254
277
|
if progress_callback:
|
|
255
|
-
progress_callback(idx + 1, total)
|
|
278
|
+
progress_callback(idx + 1, total, PRIVACY_STAGE_REDACT)
|
|
256
279
|
continue
|
|
257
280
|
record_id = str(record_id)
|
|
258
281
|
for field in fields_for_table(table):
|
|
@@ -284,10 +307,24 @@ async def run_privacy_disclosure_layer(
|
|
|
284
307
|
|
|
285
308
|
from ..sanitization.privacy_filter import PRIVACY_DISCLOSE_MAX_BATCH
|
|
286
309
|
|
|
287
|
-
|
|
288
|
-
|
|
310
|
+
# Report against the batched work, not the message list: this loop is
|
|
311
|
+
# where the model time goes, and the old callback fired only on the
|
|
312
|
+
# branches that SKIP a message -- so a run with nothing to skip
|
|
313
|
+
# reported nothing at all, for its entire duration.
|
|
314
|
+
_redact_total = len(flat_pending)
|
|
315
|
+
_redact_done = 0
|
|
316
|
+
_redact_chunk = min(PRIVACY_DISCLOSE_MAX_BATCH, PRIVACY_PROGRESS_CHUNK)
|
|
317
|
+
for i in range(0, len(flat_pending), _redact_chunk):
|
|
318
|
+
batch = flat_pending[i : i + _redact_chunk]
|
|
289
319
|
items = [{"id": e["batch_key"], "text": e["raw"]} for e in batch]
|
|
320
|
+
if progress_callback:
|
|
321
|
+
progress_callback(_redact_done, _redact_total, PRIVACY_STAGE_REDACT)
|
|
290
322
|
result = await privacy_client.redact_batch(items)
|
|
323
|
+
_redact_done += len(batch)
|
|
324
|
+
# Also after: without this the final group never reports, and the
|
|
325
|
+
# stage appears to stop one group short of its own total.
|
|
326
|
+
if progress_callback:
|
|
327
|
+
progress_callback(_redact_done, _redact_total, PRIVACY_STAGE_REDACT)
|
|
291
328
|
if result.get("status") in ("unavailable", "failed"):
|
|
292
329
|
failed_batches += 1
|
|
293
330
|
logger.warning(
|
|
@@ -349,10 +386,18 @@ async def run_privacy_disclosure_layer(
|
|
|
349
386
|
from ..sanitization.nsfw_classifier import NSFW_CLASSIFY_MAX_BATCH
|
|
350
387
|
|
|
351
388
|
nsfw_items = list(nsfw_pending.items())
|
|
352
|
-
|
|
353
|
-
|
|
389
|
+
_nsfw_total = len(nsfw_items)
|
|
390
|
+
_nsfw_done = 0
|
|
391
|
+
_nsfw_chunk = min(NSFW_CLASSIFY_MAX_BATCH, PRIVACY_PROGRESS_CHUNK)
|
|
392
|
+
for i in range(0, len(nsfw_items), _nsfw_chunk):
|
|
393
|
+
batch = nsfw_items[i : i + _nsfw_chunk]
|
|
354
394
|
items = [{"id": key, "text": entry["text"]} for key, entry in batch]
|
|
395
|
+
if progress_callback:
|
|
396
|
+
progress_callback(_nsfw_done, _nsfw_total, PRIVACY_STAGE_NSFW)
|
|
355
397
|
nsfw_result = await privacy_client.classify_nsfw_batch(items)
|
|
398
|
+
_nsfw_done += len(batch)
|
|
399
|
+
if progress_callback:
|
|
400
|
+
progress_callback(_nsfw_done, _nsfw_total, PRIVACY_STAGE_NSFW)
|
|
356
401
|
if nsfw_result.get("status") in ("failed",):
|
|
357
402
|
nsfw_failed_batches += 1
|
|
358
403
|
continue
|
|
@@ -317,7 +317,20 @@ class EntitiesJob(BaseEnrichmentJob):
|
|
|
317
317
|
from ....features.entities.edges import record_cooccurrence_pairs
|
|
318
318
|
|
|
319
319
|
for record_id, ids in entities_by_record.items():
|
|
320
|
-
|
|
320
|
+
# Tolerant lookup, like every other read of a record's time in
|
|
321
|
+
# this file. `msg_by_id` holds RAW canonical messages, and the
|
|
322
|
+
# canonical groups disagree on the column (see
|
|
323
|
+
# _EVENT_AT_FIELDS) — reading only "event_at" left every
|
|
324
|
+
# co-occurrence edge undated for any group that names it
|
|
325
|
+
# otherwise. Measured on a real import: 9,242 of 9,242
|
|
326
|
+
# co-occurrence edges had no time, while the declared lane
|
|
327
|
+
# beside them, which already used this lookup, was fully dated.
|
|
328
|
+
# Undated edges cannot be placed in a temporal view at all.
|
|
329
|
+
msg_for_event = msg_by_id.get(record_id) or {}
|
|
330
|
+
event_at = next(
|
|
331
|
+
(msg_for_event.get(f) for f in _EVENT_AT_FIELDS if msg_for_event.get(f)),
|
|
332
|
+
None,
|
|
333
|
+
)
|
|
321
334
|
for src, dst in record_cooccurrence_pairs(ids):
|
|
322
335
|
update_edge(
|
|
323
336
|
conn,
|
|
@@ -181,7 +181,11 @@ class EnrichmentOrchestrator(BaseObject):
|
|
|
181
181
|
jobs_percent = ((job_idx - 1) / total_jobs * 100) if total_jobs > 0 else 0
|
|
182
182
|
# Call orchestrator progress callback with job-level info
|
|
183
183
|
progress_callback(
|
|
184
|
-
|
|
184
|
+
# The real within-job position. This was pinned to 0
|
|
185
|
+
# with a note that it was unused -- true when the only
|
|
186
|
+
# consumer was a percentage, but a caller showing
|
|
187
|
+
# "N of M" then reads zero for a whole job.
|
|
188
|
+
processed_count=current_count,
|
|
185
189
|
total_count=total_count,
|
|
186
190
|
job_name=job_name,
|
|
187
191
|
job_percent=jobs_percent,
|
|
@@ -279,7 +283,11 @@ class EnrichmentOrchestrator(BaseObject):
|
|
|
279
283
|
job_progress_percent = (job_idx / total_jobs * 100) if total_jobs > 0 else 100
|
|
280
284
|
# Update with messages processed so far (cumulative across jobs)
|
|
281
285
|
# Job is 100% complete
|
|
282
|
-
|
|
286
|
+
# Within-job counts, matching the mid-job callback above.
|
|
287
|
+
# This passed a CUMULATIVE total against a per-job
|
|
288
|
+
# denominator, so the fourth job of ten reported
|
|
289
|
+
# "2,904 of 726" to anyone rendering the pair.
|
|
290
|
+
progress_callback(total_messages, total_messages, job_name, job_progress_percent, 100.0)
|
|
283
291
|
|
|
284
292
|
results["jobs_run"] += 1
|
|
285
293
|
except Exception as exc:
|
|
@@ -297,7 +305,10 @@ class EnrichmentOrchestrator(BaseObject):
|
|
|
297
305
|
if progress_callback:
|
|
298
306
|
job_progress_percent = (job_idx / total_jobs * 100) if total_jobs > 0 else 100
|
|
299
307
|
# Mark job as 100% complete (even if failed, we've moved past it)
|
|
300
|
-
|
|
308
|
+
# Within-job counts, as above: a failed job has still
|
|
309
|
+
# been moved past, but it did not process more messages
|
|
310
|
+
# than it was given.
|
|
311
|
+
progress_callback(total_messages, total_messages, job.get_job_name(), job_progress_percent, 100.0)
|
|
301
312
|
|
|
302
313
|
logger.debug(
|
|
303
314
|
"[PIPELINE:ENRICHMENT] %s: Enrichment complete: %d jobs run, %d total records created",
|
|
@@ -498,7 +509,25 @@ class SignalDerivationOrchestrator(EnrichmentOrchestrator):
|
|
|
498
509
|
continue
|
|
499
510
|
job_name = job.get_job_name()
|
|
500
511
|
try:
|
|
501
|
-
|
|
512
|
+
# Report like the canonical lane: a start post, a per-record
|
|
513
|
+
# callback, and a completion post. This lane used to make ONE
|
|
514
|
+
# call per job, after the job, with processed=0 and progress=100
|
|
515
|
+
# -- so every signal stage read "0 of 726" for its whole
|
|
516
|
+
# duration and the bar jumped to the end of the stage the moment
|
|
517
|
+
# it began. Observed live: "deriving emo 27, 0/726" on screen for
|
|
518
|
+
# 34 minutes while TopicsJob logged 264/726 underneath it.
|
|
519
|
+
_total_msgs = len(canonical_messages)
|
|
520
|
+
_jobs_pct_before = ((job_idx - 1) / total_jobs * 100) if total_jobs > 0 else 0.0
|
|
521
|
+
|
|
522
|
+
def _signal_job_progress(current_count: int, total_count: int, *, _name: str = job_name, _before: float = _jobs_pct_before) -> None:
|
|
523
|
+
if not progress_callback:
|
|
524
|
+
return
|
|
525
|
+
pct = (current_count / total_count * 100) if total_count > 0 else 0.0
|
|
526
|
+
progress_callback(current_count, total_count, _name, _before, pct)
|
|
527
|
+
|
|
528
|
+
if progress_callback:
|
|
529
|
+
progress_callback(0, _total_msgs, job_name, _jobs_pct_before, 0.0)
|
|
530
|
+
records = await job.enrich(canonical_messages, progress_callback=_signal_job_progress)
|
|
502
531
|
if records and records[0].get("_deferred"):
|
|
503
532
|
deferral_error = str(records[0].get("error"))
|
|
504
533
|
results["deferred_jobs"].append(job_name)
|
|
@@ -572,7 +601,8 @@ class SignalDerivationOrchestrator(EnrichmentOrchestrator):
|
|
|
572
601
|
|
|
573
602
|
await _offload_write(_settle_debt)
|
|
574
603
|
if progress_callback:
|
|
575
|
-
|
|
604
|
+
# Within-job counts at completion, matching the canonical lane.
|
|
605
|
+
progress_callback(_total_msgs, _total_msgs, job_name, (job_idx / total_jobs) * 100, 100.0)
|
|
576
606
|
except Exception as exc:
|
|
577
607
|
logger.error("[PIPELINE:SIGNAL_DERIVE] job %s failed: %s", job_name, exc)
|
|
578
608
|
results["errors"].append({"job": job_name, "error": str(exc)})
|
|
@@ -401,7 +401,15 @@ class EvidenceCache:
|
|
|
401
401
|
for row in conn.execute(
|
|
402
402
|
f"""
|
|
403
403
|
SELECT src_entity_id, dst_entity_id, edge_type, weight, evidence_count,
|
|
404
|
-
|
|
404
|
+
-- last_event_at ONLY. valid_from is when the node started
|
|
405
|
+
-- believing the relation (a recompute's clock for part_of
|
|
406
|
+
-- and other structural edges) and created_at is the row's
|
|
407
|
+
-- own birth; neither is an event. The fallback made an
|
|
408
|
+
-- undated edge report activity at rebuild time, which is
|
|
409
|
+
-- the defect fixed for the graph window in edges.py.
|
|
410
|
+
-- EvidenceCache.add tolerates None: weight still counts,
|
|
411
|
+
-- no date is invented.
|
|
412
|
+
last_event_at AS event_at
|
|
405
413
|
FROM entity_edges
|
|
406
414
|
WHERE valid_to IS NULL
|
|
407
415
|
AND LOWER(TRIM(edge_type)) IN ({placeholders})
|
|
@@ -562,11 +562,21 @@ def graph_snapshot(
|
|
|
562
562
|
clause = ""
|
|
563
563
|
else:
|
|
564
564
|
clause = " AND valid_to IS NULL"
|
|
565
|
+
# A TIME window asks "did anything happen between these dates". Only
|
|
566
|
+
# last_event_at answers that. valid_from is when the node started BELIEVING
|
|
567
|
+
# the relation -- for a structural edge written by a recompute (semantic
|
|
568
|
+
# affinity, part_of) that is the recompute's clock, and it carries no event
|
|
569
|
+
# at all. The old COALESCE fallback read that clock as activity, so every
|
|
570
|
+
# entity touched by a rebuild appeared in "the last 11 days" no matter when
|
|
571
|
+
# it was last mentioned: 69 of 4,602 dormant entities on the reference node,
|
|
572
|
+
# 60 of them through exactly these undated edges. An undated edge is no
|
|
573
|
+
# evidence of activity in any window; it stays in the unwindowed views,
|
|
574
|
+
# where valid_from remains the right recency key for ORDER BY.
|
|
565
575
|
if after:
|
|
566
|
-
clause += " AND
|
|
576
|
+
clause += " AND last_event_at IS NOT NULL AND last_event_at >= ?"
|
|
567
577
|
where_params.append(after)
|
|
568
578
|
if before:
|
|
569
|
-
clause += " AND
|
|
579
|
+
clause += " AND last_event_at IS NOT NULL AND last_event_at <= ?"
|
|
570
580
|
where_params.append(before)
|
|
571
581
|
|
|
572
582
|
where_sql = (
|