topos-node 1.3.1__tar.gz → 1.3.3__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.1 → topos_node-1.3.3}/MANIFEST.in +2 -0
- {topos_node-1.3.1/topos_node.egg-info → topos_node-1.3.3}/PKG-INFO +9 -1
- {topos_node-1.3.1 → topos_node-1.3.3}/pyproject.toml +13 -1
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/__version__.py +2 -2
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/llm.py +47 -0
- topos_node-1.3.3/topos/api/shell.py +91 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/signal.py +195 -2
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/uma_data.py +4 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/app.py +2 -0
- topos_node-1.3.3/topos/cli/assets/topos_blk_rounded.png +0 -0
- topos_node-1.3.3/topos/cli/assets/topos_white.png +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/cli/commands.py +90 -2
- topos_node-1.3.3/topos/cli/tray.py +392 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/__init__.py +2 -0
- topos_node-1.3.3/topos/core/handlers/complexity.py +145 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/messages.py +3 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/signal_features.py +243 -1
- topos_node-1.3.3/topos/core/handlers/timeline_daily.py +40 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/uma.py +15 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/logging.py +47 -7
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/state.py +34 -2
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/engine.py +93 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/catalog.py +13 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/__init__.py +2 -0
- topos_node-1.3.3/topos/enrichment/jobs/canonical/complexity_snapshot_job.py +83 -0
- topos_node-1.3.3/topos/features/complexity/__init__.py +10 -0
- topos_node-1.3.3/topos/features/complexity/canonical.py +301 -0
- topos_node-1.3.3/topos/features/complexity/engine.py +287 -0
- topos_node-1.3.3/topos/features/complexity/entropy.py +100 -0
- topos_node-1.3.3/topos/features/complexity/focus.py +507 -0
- topos_node-1.3.3/topos/features/complexity/graph_metrics.py +177 -0
- topos_node-1.3.3/topos/features/complexity/influence.py +664 -0
- topos_node-1.3.3/topos/features/complexity/projection.py +567 -0
- topos_node-1.3.3/topos/features/complexity/snapshots.py +77 -0
- topos_node-1.3.3/topos/features/complexity/store.py +136 -0
- topos_node-1.3.3/topos/features/complexity/topic_merge.py +437 -0
- topos_node-1.3.3/topos/features/complexity/topics.py +154 -0
- topos_node-1.3.3/topos/features/complexity/topics_daily.py +106 -0
- topos_node-1.3.3/topos/features/complexity/volatility.py +140 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/edges.py +18 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/reads.py +73 -5
- topos_node-1.3.3/topos/features/lifecycle/blackhole.py +533 -0
- topos_node-1.3.3/topos/features/lifecycle/blackhole_guard.py +385 -0
- topos_node-1.3.3/topos/features/lifecycle/blackhole_llm.py +169 -0
- topos_node-1.3.3/topos/features/lifecycle/blackhole_rebuild.py +210 -0
- topos_node-1.3.3/topos/features/timeline_rollup.py +121 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/triage/badges.py +7 -3
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/triage/dashboard.py +21 -6
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/triage/intents.py +83 -6
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/protocol/handled_message_types.json +13 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/game_layer.py +14 -12
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/pipeline.py +3 -1
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/retrieval.py +275 -24
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/scope_registry.json +34 -8
- topos_node-1.3.3/topos/storage/db/migrations/__init__.py +190 -0
- topos_node-1.3.3/topos/storage/db/migrations/backup.py +125 -0
- topos_node-1.3.3/topos/storage/db/migrations/complexity_v1.py +48 -0
- topos_node-1.3.3/topos/storage/db/migrations/entity_blackhole_v1.py +91 -0
- topos_node-1.3.3/topos/storage/db/migrations/registry.py +318 -0
- topos_node-1.3.3/topos/storage/db/migrations/registry_checksums.json +31 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/uma_contact_enrichment.py +22 -2
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/upgrades/__init__.py +49 -15
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/upgrades/manifests.json +63 -9
- {topos_node-1.3.1 → topos_node-1.3.3/topos_node.egg-info}/PKG-INFO +9 -1
- {topos_node-1.3.1 → topos_node-1.3.3}/topos_node.egg-info/SOURCES.txt +31 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos_node.egg-info/requires.txt +8 -0
- topos_node-1.3.1/topos/storage/db/migrations/__init__.py +0 -310
- {topos_node-1.3.1 → topos_node-1.3.3}/LICENSE +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/README.md +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/setup.cfg +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/shared/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/shared/filtering.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/shared/schema_registry.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/tests/test_extensions.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/tests/test_llm_integrations_storage.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/tests/test_protocol_handled_types.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/analytics/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/analytics/duckdb_adapter.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/analytics/messenger_communities.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/analytics/messenger_graph.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/analytics/messenger_labels.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/analytics/profiles.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/analytics/query_engine.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/analytics/raw_queries.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/analytics.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/app_registry.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/backup.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/compute_remote.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/conversation_context.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/data_commit.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/data_explorer_table_prefs.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/database_explorer.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/db.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/device.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/engine_run.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/enrichment.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/enrichment_lab.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/facts_llm_config.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/filter_lab.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/health.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/home_chat.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/ingestion_api.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/ingestion_compat.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/ingestion_sources.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/intelligence_lifecycle.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/local_mcp.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/messenger_analytics.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/privacy_disclose.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/query_api.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/runtime_bootstrap.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/sanitization_ollama_config.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/signal_extraction_config.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/source_install.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/source_scrub.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/sources.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/sources_overview.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/sync.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/tool_index.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/ui_config.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/upgrades.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/usage.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/api/user_identity.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/auth.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/mappers/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/mappers/base.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/mappers/browser_activity_mapper.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/mappers/chatgpt_mapper.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/mappers/demo_mappers.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/mappers/documents_mapper.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/mappers/github_activity_mapper.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/mappers/google_calendar_mapper.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/mappers/grok_mapper.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/mappers/messenger_mapper.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/models.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/canonicalization/resolver.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/cli/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/cli/__main__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/config/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/config/conversation_context_llm.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/config/facts_llm.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/config/sanitization_ollama.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/config/settings.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/config/signal_extraction.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/config/uvicorn_logging.json +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/contacts/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/contacts/identity.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/control_plane_client.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/api_models.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/connection_resilience.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/device_helpers.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/errors.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/events.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/common.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/config.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/contacts_import.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/database_explorer.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/device.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/enrichment.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/enrichment_lab.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/filter_lab.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/home_chat.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/ingest.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/messenger_analytics.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/query.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/registry.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/routines.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/sources.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/handlers/tool_index.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/metrics.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/routine_access.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/startup_banner.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/table_layers.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/core/types.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/data_explorer_row_delete.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/data_explorer_table_prefs.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/data_explorer_tables.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/disclosure/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/disclosure/canonical_writer.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/disclosure/content_policy.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/disclosure/field_registry.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/disclosure/privacy_layer.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/disclosure/tier.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/backends/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/backends/base.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/backends/generative_prompts.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/backends/generative_response.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/backends/huggingface.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/backends/ollama.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/backends/openai.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/backends/openai_compatible.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/backends/redpill.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/backends/stub.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/client.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/intake.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/memory_utils.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/model_cache.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/pipeline_memory.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/queue_manager.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/registration.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/result_formatter.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/router.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/scoped_token.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/tasks.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/transport.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/usage_guard.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/usage_observation.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/engine/validator.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/derived_tables.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/job_writer.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/base.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/_batch_limits.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/_engine_runner.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/attention_triage_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/availability_scores_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/brief_fallback.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/dimension_summary_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/embeddings_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/emo_27_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/entities_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/fact_extraction_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/goal_extraction_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/pii_redaction_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/relationship_edges_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/sentiment_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/statistics_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/timeline_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/topic_clusters_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/topics_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/url_classification_core.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/url_classification_enrichment_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/canonical/url_classification_signal_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/raw/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/raw/attachments_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/raw/language_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/raw/time_normalization_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/jobs/raw/tool_calls_job.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/model_overrides.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/models/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/models/manager.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/models/mvp_defaults.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/models/registry.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/models/versioning.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/orchestrator.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/processor.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/progress_bar.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/registry_contract.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/signal_derivation_config.json +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/source_overrides.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment/website_classifier.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment_lab/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment_lab/bundles.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment_lab/model_resolve.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment_lab/schema.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment_lab/service.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment_lab/store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/enrichment_lab/worker.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/evals/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/evals/enrichment_eval.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/extensions.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/consolidation.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/declared_mappings.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/dossier.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/fact_materializer.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/graph_enrichers.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/graph_refresh.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/graph_search.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/linking.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/maintenance.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/entities/resolver.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/facts/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/facts/extract.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/facts/llm_extract.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/facts/reads.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/facts/store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/facts/verdicts.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/fit/evaluator.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/fit/opportunity_types.json +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/lifecycle/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/lifecycle/derived_scrub.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/lifecycle/exclusions.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/lifecycle/gc.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/provenance/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/provenance/posture.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/provenance/roles.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/brief_canonical_loader.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/brief_ontology.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/brief_renderer.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/brief_schemas.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/chunking.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/cluster_labels.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/conversation_context.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/data_health.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/definitions/brief_ontologies.json +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/definitions/intentions.json +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/definitions/profile.json +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/definitions/relationships.json +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/definitions/schema/dimension_definition.schema.json +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/definitions/time.json +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/definitions/work.json +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/dimension_briefs.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/dimension_definition_loader.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/dimension_profiles.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/dimension_registry.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/embed_context.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/extraction/artifact_router.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/extraction/artifact_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/extraction/rule_extractors.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/graph_sanitize.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/hybrid_search.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/incremental_clustering.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/model_recommendations.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/query_embed_cache.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/schemas.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/service.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/signal_object_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/source_hydration.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/structured_signal.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/tool_index.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/topic_clustering.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/typed_stores/aggregates.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/typed_stores/commitments.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/typed_stores/rhythm.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/typed_stores/scope_materializer.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/vector_codec.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/vector_math.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/signal/vector_settings.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/stats/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/stats/definitions.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/stats/engine.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/stats/fold.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/stats/insights.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/timeline_projection.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/triage/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/triage/daily.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/triage/instruments.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/features/triage/readiness.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/filter_lab/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/filter_lab/bundles.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/filter_lab/schema.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/filter_lab/service.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/filter_lab/store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/filter_lab/worker.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/home_chat/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/home_chat/schema.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/home_chat/store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/hosted_pool_lease.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/canonical_pipeline.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/checkpoints/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/checkpoints/checkpoint_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/checkpoints/sqlite_checkpoint_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/inbox_crypto.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/inbox_drain.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/ingest_helpers.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/jobs.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/journal_location_fanout.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/journal_time_log_normalize.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/local_sync.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/log_preview.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/manager.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parser.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/base.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/browser_parser.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/calendar_parser.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/chatgpt_conversation_flattener.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/chatgpt_parser.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/demo_file_parsers.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/documents_parser.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/github_activity_parser.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/google_calendar_parser.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/grok_parser.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/messenger_parser.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/parsers/voxterm_parser.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/progress.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/reprocess.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/sources/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/sources/base.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/sources/calendar.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/sources/chatgpt.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/sources/contact_importers.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/sources/grok.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/sources/imessage_reader.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/sources/signal_export_parser.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/sources/signal_reader.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/state_machine.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/triggers/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/triggers/file_trigger.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/triggers/sqlite_trigger.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/usage_inbox_dedupe.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/validation/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/validation/base.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/validation/schema_registry.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/ingestion/validation/schema_validator.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/lineage/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/lineage/provenance.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/lineage/tracker.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/llm_integrations_storage.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/mcp_stdio_proxy.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/observability/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/observability/alerts.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/observability/metrics.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/observability/tracing.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/openai_client.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/pipeline/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/pipeline/audit.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/pipeline/envelope.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/pipeline/job_runner.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/pipeline/job_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/pipeline/stages.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/pipeline/stub_enqueue.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/projections/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/projections/vector_index/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/projections/vector_index/base.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/projections/vector_index/builders.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/projections/vector_index/health_checks.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/protocol/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/audit.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/ddr.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/disclosure.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/fingerprint.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/grant_cache.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/inference.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/intent.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/manifest.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/manifest_validation.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/minimizer.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/negotiation.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/planner.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/runtime.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/scope_registry_loader.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/session.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/session_utils.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/source_generation.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/truth_facts.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/truth_prompts.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/turn_classifier.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/types.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/verify.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/query/verify_modes.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/rate_limit.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/routines/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/routines/schema.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/routines/store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/runtime_housekeeping.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/runtime_shutdown.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/runtime_update.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sanitization/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sanitization/nsfw_classifier.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sanitization/ollama_transforms.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sanitization/prewarm.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sanitization/privacy_filter.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/scope_resolution.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/container.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/embeddings/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/embeddings/base.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/embeddings/local.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/embeddings/remote.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/interfaces.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/llm/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/llm/base.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/llm/openai.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/local.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/services/postgres.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sources/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sources/bundled_canonical_triples.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sources/canonical_signal_defaults.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sources/definitions.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sources/install_service.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sources/registry.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sources/runtime_install.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sources/scrub_attribution.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sources/scrub_service.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/startup_banner.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/adapters/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/adapters/factory.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/adapters/fakes.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/adapters/postgres/stores.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/adapters/protocols.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/adapters/sqlite/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/adapters/sqlite/stores.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/adapters/sqlite/vector_search.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/activity_tables.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/ai_chat/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/ai_chat/canonicalizer.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/ai_chat/mapper.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/ai_chat/model.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/ai_chat/tables.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/calendar_contacts_tables.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/canonical_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/conversations_tables.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/mapping_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/canonical/postgres.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/client.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/connection_tuning.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/activity_events_content_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/actor_role_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/attention_triage_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/attention_triage_v2.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/calendar_events_scheduling_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/canonical_address_book_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/canonical_disclosure_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/canonical_nsfw_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/conversation_context_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/derivation_ledger_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/documents_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/entity_edges_validity_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/episodes_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/extraction_artifacts.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/journal_entries_duration_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/journal_entries_ends_at_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/journal_entries_people_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/journal_entries_place_name_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/journal_entries_starts_at_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/pipeline_jobs_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/remediation_person_model.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/scope_source_generation.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/signal_dimension_backfill_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/signal_dimension_harness.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/signal_objects.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/signal_objects_period_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/signal_objects_updated_by_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/stage9_column_renames.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/vector_storage_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/vector_storage_v2.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/vector_storage_v3.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/vector_storage_v4.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_clusters_v2.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_entities_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_entity_review_v2.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_facts_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_lifecycle_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_mvp_phase0.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_mvp_phase1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_mvp_phase4_messages_cutover.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_mvp_phase5_topic_clusters.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_mvp_phase6_dimension_briefs.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_mvp_query_quality.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_mvp_topic_clusters_coordination.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_stats_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/migrations/wiki_timeline_v1.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/paths.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/postgres.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/schema.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/storage_breakdown.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/db/write_gate.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/enrichment/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/enrichment/canonical_enrichment_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/enrichment/raw_enrichment_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/normalized/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/normalized/normalized_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/oplog/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/oplog/decision.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/oplog/oplog_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/oplog/postgres.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/person/identity_resolver.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/projections/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/projections/index_ops_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/projections/vector_index_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/raw/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/raw/browser_flat_tables.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/raw/file_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/raw/raw_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/raw/raw_tables_manager.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/raw/sqlite_raw_store.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/security/encryption.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/signal_identity.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/source_settings.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/storage/user_identity.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sync/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sync/client.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/sync_handlers.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/testing/__init__.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/testing/lifespan.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/uma_filters.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/uma_resource_id.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/uma_rpt.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/upgrades/runner.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/utils/base_object.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos/websocket_client.py +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos_node.egg-info/dependency_links.txt +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos_node.egg-info/entry_points.txt +0 -0
- {topos_node-1.3.1 → topos_node-1.3.3}/topos_node.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: topos-node
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
4
4
|
Summary: Topos personal AI engine (FastAPI): local data, sync, control plane WebSocket client
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Project-URL: Homepage, https://topos.dialogues.ai
|
|
@@ -33,7 +33,12 @@ Requires-Dist: hf-xet~=1.5.1
|
|
|
33
33
|
Requires-Dist: sentence-transformers~=5.6.0
|
|
34
34
|
Requires-Dist: networkx<3.7,>=3.2.1
|
|
35
35
|
Requires-Dist: python-louvain<0.17,>=0.16
|
|
36
|
+
Requires-Dist: numpy<3,>=1.26
|
|
37
|
+
Requires-Dist: scipy<2,>=1.11
|
|
38
|
+
Requires-Dist: scikit-learn<2,>=1.4
|
|
36
39
|
Requires-Dist: sqlite-vec~=0.1.9
|
|
40
|
+
Requires-Dist: pystray~=0.19.5
|
|
41
|
+
Requires-Dist: pillow>=10.0
|
|
37
42
|
Provides-Extra: database
|
|
38
43
|
Requires-Dist: psycopg[binary]~=3.3.3; extra == "database"
|
|
39
44
|
Provides-Extra: engine
|
|
@@ -46,6 +51,9 @@ Requires-Dist: hf-xet~=1.5.1; extra == "engine"
|
|
|
46
51
|
Requires-Dist: sentence-transformers~=5.6.0; extra == "engine"
|
|
47
52
|
Requires-Dist: networkx<3.7,>=3.2.1; extra == "engine"
|
|
48
53
|
Requires-Dist: python-louvain<0.17,>=0.16; extra == "engine"
|
|
54
|
+
Requires-Dist: numpy<3,>=1.26; extra == "engine"
|
|
55
|
+
Requires-Dist: scipy<2,>=1.11; extra == "engine"
|
|
56
|
+
Requires-Dist: scikit-learn<2,>=1.4; extra == "engine"
|
|
49
57
|
Provides-Extra: local
|
|
50
58
|
Provides-Extra: signal
|
|
51
59
|
Requires-Dist: pysqlcipher3~=1.2.0; extra == "signal"
|
|
@@ -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.3"
|
|
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"
|
|
@@ -35,7 +35,15 @@ dependencies = [
|
|
|
35
35
|
"sentence-transformers~=5.6.0",
|
|
36
36
|
"networkx>=3.2.1,<3.7",
|
|
37
37
|
"python-louvain>=0.16,<0.17",
|
|
38
|
+
# Complexity readings (topos.features.complexity): entropy/MI + graph math.
|
|
39
|
+
"numpy>=1.26,<3",
|
|
40
|
+
"scipy>=1.11,<2",
|
|
41
|
+
"scikit-learn>=1.4,<2",
|
|
38
42
|
"sqlite-vec~=0.1.9",
|
|
43
|
+
# Menu-bar/system-tray status icon (topos.cli.tray). Imported lazily and
|
|
44
|
+
# only when a GUI session is detected — harmless on headless installs.
|
|
45
|
+
"pystray~=0.19.5",
|
|
46
|
+
"pillow>=10.0",
|
|
39
47
|
]
|
|
40
48
|
|
|
41
49
|
[project.urls]
|
|
@@ -58,6 +66,9 @@ engine = [
|
|
|
58
66
|
"sentence-transformers~=5.6.0",
|
|
59
67
|
"networkx>=3.2.1,<3.7",
|
|
60
68
|
"python-louvain>=0.16,<0.17",
|
|
69
|
+
"numpy>=1.26,<3",
|
|
70
|
+
"scipy>=1.11,<2",
|
|
71
|
+
"scikit-learn>=1.4,<2",
|
|
61
72
|
]
|
|
62
73
|
# Backward-compatible alias; local runtime deps are in core dependencies.
|
|
63
74
|
local = []
|
|
@@ -109,6 +120,7 @@ markers = [
|
|
|
109
120
|
"adversarial: scope-break / access-control adversarial test (privacy enforcement)",
|
|
110
121
|
"uar: unauthorized-access-rate probe (privacy claim #1); tier-1 CI gate, must find zero leaks",
|
|
111
122
|
"cer: canary-extraction-rate probe (privacy claim #2); tier-1 CI gate",
|
|
123
|
+
"bhlr: black-hole leak-rate probe (entity black hole); tier-1 CI gate, must find zero leaks",
|
|
112
124
|
"eval_nightly: statistical privacy eval (attacker advantage, minimality, latency); nightly",
|
|
113
125
|
"eval_release: release-tier privacy eval (negotiation A/B, Pareto); run per release",
|
|
114
126
|
]
|
|
@@ -16,6 +16,51 @@ logger = logging.getLogger("topos.api.llm")
|
|
|
16
16
|
router = APIRouter()
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
def _enforce_blackhole_egress(body: GenerationRequest) -> GenerationRequest:
|
|
20
|
+
"""Gate B: this route reaches cloud providers without passing the engine router.
|
|
21
|
+
|
|
22
|
+
`Engine.run` gates every task-shaped inference, but these two endpoints call
|
|
23
|
+
the LLM service directly, so the same policy has to be applied here or the
|
|
24
|
+
hole stays open. Rewrites the provider onto a secure one when the prompt
|
|
25
|
+
mentions a protected entity; refuses when nothing secure can serve it.
|
|
26
|
+
|
|
27
|
+
The refusal is owner-facing (this route is API-key authenticated) and still
|
|
28
|
+
names no entity — the reason may be logged somewhere less private.
|
|
29
|
+
"""
|
|
30
|
+
from fastapi import HTTPException
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
from ..engine.engine import _policy_db_connection
|
|
34
|
+
from ..features.lifecycle.blackhole_llm import describe_block, evaluate
|
|
35
|
+
except Exception: # noqa: BLE001
|
|
36
|
+
return body
|
|
37
|
+
|
|
38
|
+
conn, owned = _policy_db_connection()
|
|
39
|
+
try:
|
|
40
|
+
verdict = evaluate(
|
|
41
|
+
conn,
|
|
42
|
+
{"prompt": body.prompt, "system": getattr(body, "system", None)},
|
|
43
|
+
provider=body.provider or "",
|
|
44
|
+
)
|
|
45
|
+
except Exception as exc: # noqa: BLE001
|
|
46
|
+
raise HTTPException(status_code=503, detail=f"blackhole_policy_unavailable: {exc}")
|
|
47
|
+
finally:
|
|
48
|
+
if owned and conn is not None:
|
|
49
|
+
try:
|
|
50
|
+
conn.close()
|
|
51
|
+
except Exception: # noqa: BLE001
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
if not verdict.tainted:
|
|
55
|
+
return body
|
|
56
|
+
if verdict.blocked:
|
|
57
|
+
raise HTTPException(status_code=503, detail=describe_block(verdict))
|
|
58
|
+
if verdict.redirected:
|
|
59
|
+
logger.info("blackhole: llm route redirected to %s", verdict.provider)
|
|
60
|
+
return body.model_copy(update={"provider": verdict.provider, "model": None})
|
|
61
|
+
return body
|
|
62
|
+
|
|
63
|
+
|
|
19
64
|
@router.post(
|
|
20
65
|
"/llm_generation",
|
|
21
66
|
response_model=GenerationResponse,
|
|
@@ -28,6 +73,7 @@ async def llm_generation(body: GenerationRequest):
|
|
|
28
73
|
body.model,
|
|
29
74
|
len(body.prompt or ""),
|
|
30
75
|
)
|
|
76
|
+
body = _enforce_blackhole_egress(body)
|
|
31
77
|
services: Services = get_services()
|
|
32
78
|
result = await services.llm.generate(body.model_dump())
|
|
33
79
|
usage = result.get("usage") if isinstance(result, dict) else {}
|
|
@@ -60,6 +106,7 @@ async def llm_generation_alias(body: GenerationRequest):
|
|
|
60
106
|
body.model,
|
|
61
107
|
len(body.prompt or ""),
|
|
62
108
|
)
|
|
109
|
+
body = _enforce_blackhole_egress(body)
|
|
63
110
|
services: Services = get_services()
|
|
64
111
|
result = await services.llm.generate(body.model_dump())
|
|
65
112
|
usage = result.get("usage") if isinstance(result, dict) else {}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Shell contract endpoints.
|
|
2
|
+
|
|
3
|
+
Everything a menu-bar/tray shell (the pystray tray, the future Swift macOS
|
|
4
|
+
shell, the Windows shell) needs to supervise this node lives here, so shells
|
|
5
|
+
stay dumb renderers: identity + version for attach-don't-double-start, the
|
|
6
|
+
log file location for a "Show Logs" action, and cached update state with a
|
|
7
|
+
localhost-only trigger to apply an update.
|
|
8
|
+
|
|
9
|
+
Update *checking* is not done here — the runtime update monitor started in
|
|
10
|
+
app startup keeps ``get_update_info()`` fresh; this router only reads it.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
import threading
|
|
17
|
+
from time import time
|
|
18
|
+
|
|
19
|
+
from fastapi import APIRouter, HTTPException, Request
|
|
20
|
+
|
|
21
|
+
from ..core.logging import get_log_file_path
|
|
22
|
+
from ..runtime_update import (
|
|
23
|
+
DEFAULT_PACKAGE_NAME,
|
|
24
|
+
apply_package_update,
|
|
25
|
+
get_runtime_version,
|
|
26
|
+
get_update_info,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
router = APIRouter(prefix="/v1/shell", tags=["shell"])
|
|
30
|
+
|
|
31
|
+
_STARTED_AT = time()
|
|
32
|
+
|
|
33
|
+
_apply_lock = threading.Lock()
|
|
34
|
+
_apply_state: dict[str, object] = {"applying": False, "last_result": None}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _client_is_local(request: Request) -> bool:
|
|
38
|
+
# Applying an update is the one mutating action here; a LAN peer must not
|
|
39
|
+
# be able to trigger it even though the node binds 0.0.0.0. ("testclient"
|
|
40
|
+
# is FastAPI's TestClient peer name — unreachable over a real socket.)
|
|
41
|
+
host = request.client.host if request.client else None
|
|
42
|
+
return host in ("127.0.0.1", "::1", "localhost", "testclient")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@router.get("/status")
|
|
46
|
+
async def shell_status() -> dict:
|
|
47
|
+
info = get_update_info()
|
|
48
|
+
log_file = get_log_file_path()
|
|
49
|
+
return {
|
|
50
|
+
"name": "topos-node",
|
|
51
|
+
"version": get_runtime_version(),
|
|
52
|
+
"pid": os.getpid(),
|
|
53
|
+
"started_at": _STARTED_AT,
|
|
54
|
+
"log_file": str(log_file) if log_file else None,
|
|
55
|
+
"update": {
|
|
56
|
+
"available": info is not None,
|
|
57
|
+
"installed": info.installed if info else None,
|
|
58
|
+
"latest": info.latest if info else None,
|
|
59
|
+
"applying": _apply_state["applying"],
|
|
60
|
+
"last_result": _apply_state["last_result"],
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@router.post("/update")
|
|
66
|
+
async def shell_apply_update(request: Request) -> dict:
|
|
67
|
+
if not _client_is_local(request):
|
|
68
|
+
raise HTTPException(
|
|
69
|
+
status_code=403, detail="Shell update can only be triggered from localhost."
|
|
70
|
+
)
|
|
71
|
+
if get_update_info() is None:
|
|
72
|
+
return {"started": False, "reason": "no_update_available"}
|
|
73
|
+
with _apply_lock:
|
|
74
|
+
if _apply_state["applying"]:
|
|
75
|
+
return {"started": False, "reason": "already_applying"}
|
|
76
|
+
_apply_state["applying"] = True
|
|
77
|
+
_apply_state["last_result"] = None
|
|
78
|
+
|
|
79
|
+
def worker() -> None:
|
|
80
|
+
ok = False
|
|
81
|
+
try:
|
|
82
|
+
ok = apply_package_update(DEFAULT_PACKAGE_NAME)
|
|
83
|
+
finally:
|
|
84
|
+
with _apply_lock:
|
|
85
|
+
_apply_state["applying"] = False
|
|
86
|
+
# The running process keeps the old version until restart;
|
|
87
|
+
# shells surface "restart to finish" off this value.
|
|
88
|
+
_apply_state["last_result"] = "success" if ok else "failed"
|
|
89
|
+
|
|
90
|
+
threading.Thread(target=worker, daemon=True, name="topos-shell-update").start()
|
|
91
|
+
return {"started": True}
|
|
@@ -217,9 +217,12 @@ async def list_entities(
|
|
|
217
217
|
):
|
|
218
218
|
"""Resolved entity registry (entity spine), sorted by mention count."""
|
|
219
219
|
from ..features.entities.reads import list_entities as _list
|
|
220
|
+
from ..features.lifecycle.blackhole_guard import owner_ui_guard
|
|
220
221
|
|
|
222
|
+
conn = _entities_conn()
|
|
221
223
|
return _list(
|
|
222
|
-
|
|
224
|
+
conn,
|
|
225
|
+
guard=owner_ui_guard(conn),
|
|
223
226
|
q=q,
|
|
224
227
|
entity_type=entity_type,
|
|
225
228
|
contacts_only=contacts_only,
|
|
@@ -253,11 +256,13 @@ async def get_entity_graph(
|
|
|
253
256
|
import asyncio
|
|
254
257
|
|
|
255
258
|
from ..features.entities.reads import entity_graph
|
|
259
|
+
from ..features.lifecycle.blackhole_guard import owner_ui_guard
|
|
256
260
|
|
|
257
261
|
conn = _entities_conn()
|
|
258
262
|
return await asyncio.to_thread(
|
|
259
263
|
entity_graph,
|
|
260
264
|
conn,
|
|
265
|
+
guard=owner_ui_guard(conn),
|
|
261
266
|
limit_nodes=limit_nodes,
|
|
262
267
|
limit_edges=limit_edges,
|
|
263
268
|
min_weight=min_weight,
|
|
@@ -347,8 +352,10 @@ async def get_entity(
|
|
|
347
352
|
):
|
|
348
353
|
"""Entity detail: aliases, connections, recent mentions, dossier (owner view)."""
|
|
349
354
|
from ..features.entities.reads import get_entity_detail
|
|
355
|
+
from ..features.lifecycle.blackhole_guard import owner_ui_guard
|
|
350
356
|
|
|
351
|
-
|
|
357
|
+
conn = _entities_conn()
|
|
358
|
+
detail = get_entity_detail(conn, entity_id, guard=owner_ui_guard(conn))
|
|
352
359
|
if detail is None:
|
|
353
360
|
raise HTTPException(status_code=404, detail=f"entity not found: {entity_id}")
|
|
354
361
|
return detail
|
|
@@ -458,6 +465,79 @@ async def exclude_entity_from_intelligence(
|
|
|
458
465
|
raise HTTPException(status_code=400, detail=str(exc))
|
|
459
466
|
|
|
460
467
|
|
|
468
|
+
class EntityBlackholeBody(BaseModel):
|
|
469
|
+
processing_tier: str = Field(default="secure", max_length=20)
|
|
470
|
+
note: Optional[str] = Field(default=None, max_length=500)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
@router.post("/entities/{entity_id}/blackhole")
|
|
474
|
+
async def blackhole_entity(
|
|
475
|
+
entity_id: str,
|
|
476
|
+
body: EntityBlackholeBody = EntityBlackholeBody(),
|
|
477
|
+
_api_key: str = Depends(require_api_key),
|
|
478
|
+
):
|
|
479
|
+
"""Owner black hole: this entity is mine alone.
|
|
480
|
+
|
|
481
|
+
Nothing is deleted — the owner keeps full visibility. Every other caller
|
|
482
|
+
(third-party MCP agents, routines, grantees) is denied, and content
|
|
483
|
+
mentioning it may only be processed by the tier's secure model set.
|
|
484
|
+
Raises a rebuild-needed notification before the rebuild runs (D4).
|
|
485
|
+
"""
|
|
486
|
+
import asyncio
|
|
487
|
+
|
|
488
|
+
from ..features.lifecycle.blackhole import BlackholeStore
|
|
489
|
+
from ..features.lifecycle.blackhole_rebuild import rebuild_for_blackhole
|
|
490
|
+
|
|
491
|
+
conn = _entities_conn()
|
|
492
|
+
try:
|
|
493
|
+
result = BlackholeStore(conn).blackhole_entity(
|
|
494
|
+
entity_ref=entity_id,
|
|
495
|
+
processing_tier=body.processing_tier,
|
|
496
|
+
note=body.note,
|
|
497
|
+
)
|
|
498
|
+
except ValueError as exc:
|
|
499
|
+
raise HTTPException(status_code=400, detail=str(exc))
|
|
500
|
+
|
|
501
|
+
# D4: the notification above was raised first, so the owner already knows the
|
|
502
|
+
# hide is incomplete; now do the rebuild that makes it true. Non-owners are
|
|
503
|
+
# withheld the prose artifacts for the duration, so this running late is safe
|
|
504
|
+
# while it running silently would not be.
|
|
505
|
+
if not result.get("already_blackholed"):
|
|
506
|
+
result["rebuild"] = await asyncio.to_thread(rebuild_for_blackhole, conn, entity_id)
|
|
507
|
+
result["rebuild"] = result["rebuild"].as_dict()
|
|
508
|
+
return result
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
@router.delete("/entities/{entity_id}/blackhole")
|
|
512
|
+
async def unblackhole_entity(
|
|
513
|
+
entity_id: str,
|
|
514
|
+
_api_key: str = Depends(require_api_key),
|
|
515
|
+
):
|
|
516
|
+
"""Lift a black hole. Existing grants are not restored — normal permissions resume."""
|
|
517
|
+
from ..features.lifecycle.blackhole import BlackholeStore
|
|
518
|
+
|
|
519
|
+
return BlackholeStore(_entities_conn()).unblackhole_entity(entity_ref=entity_id)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
@router.get("/blackholes")
|
|
523
|
+
async def list_blackholes(_api_key: str = Depends(require_api_key)):
|
|
524
|
+
"""The owner's off-limits list (mirrors the exclusions list in the drawer)."""
|
|
525
|
+
from ..features.lifecycle.blackhole import BlackholeStore
|
|
526
|
+
|
|
527
|
+
store = BlackholeStore(_entities_conn())
|
|
528
|
+
return {"blackholes": store.list(), "notifications": store.notifications(state="open")}
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
@router.post("/blackholes/notifications/{notification_id}/dismiss")
|
|
532
|
+
async def dismiss_blackhole_notification(
|
|
533
|
+
notification_id: str,
|
|
534
|
+
_api_key: str = Depends(require_api_key),
|
|
535
|
+
):
|
|
536
|
+
from ..features.lifecycle.blackhole import BlackholeStore
|
|
537
|
+
|
|
538
|
+
return {"dismissed": BlackholeStore(_entities_conn()).dismiss_notification(notification_id)}
|
|
539
|
+
|
|
540
|
+
|
|
461
541
|
@router.get("/facts")
|
|
462
542
|
async def list_facts(
|
|
463
543
|
predicate: Optional[str] = Query(default=None, max_length=80),
|
|
@@ -653,6 +733,23 @@ async def refresh_dimension_brief(
|
|
|
653
733
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
654
734
|
|
|
655
735
|
|
|
736
|
+
# --------------------------------------------------------------- timeline rollup
|
|
737
|
+
|
|
738
|
+
@router.get("/timeline/daily")
|
|
739
|
+
async def timeline_daily(
|
|
740
|
+
_api_key: str = Depends(require_api_key),
|
|
741
|
+
days: int = Query(90, ge=1, le=365),
|
|
742
|
+
):
|
|
743
|
+
"""Per-day lane counts + births + episodes (PLAN_TIMELINE_UNIFIED.md E1)."""
|
|
744
|
+
from ..core.state import get_db_connection
|
|
745
|
+
from ..features.timeline_rollup import timeline_daily_rollup
|
|
746
|
+
|
|
747
|
+
conn = get_db_connection()
|
|
748
|
+
if conn is None:
|
|
749
|
+
raise HTTPException(status_code=503, detail="database unavailable")
|
|
750
|
+
return timeline_daily_rollup(conn, days=days)
|
|
751
|
+
|
|
752
|
+
|
|
656
753
|
# --------------------------------------------------------------- attention triage
|
|
657
754
|
|
|
658
755
|
@router.get("/attention/dashboard")
|
|
@@ -669,3 +766,99 @@ async def attention_dashboard(
|
|
|
669
766
|
if conn is None:
|
|
670
767
|
raise HTTPException(status_code=503, detail="database unavailable")
|
|
671
768
|
return attention_dashboard_data(conn, days=days, include_titles=include_titles)
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
# --------------------------------------------------------------- complexity
|
|
772
|
+
|
|
773
|
+
@router.get("/complexity/summary")
|
|
774
|
+
async def complexity_summary(
|
|
775
|
+
_api_key: str = Depends(require_api_key),
|
|
776
|
+
recompute: bool = Query(False),
|
|
777
|
+
weeks: int = Query(12, ge=1, le=104),
|
|
778
|
+
window_days: int = Query(30, ge=1, le=3650),
|
|
779
|
+
half_life_days: Optional[float] = Query(None, ge=0.1, le=3650.0),
|
|
780
|
+
):
|
|
781
|
+
"""Data spine for the /data/complexity tab (PLAN_COMPLEXITY_DATA_PAGE.md).
|
|
782
|
+
|
|
783
|
+
Snapshot computation is CPU-bound (~1s cold on a live-size DB), so it runs
|
|
784
|
+
in a worker thread; warm calls serve the complexity_snapshots cache.
|
|
785
|
+
"""
|
|
786
|
+
import asyncio
|
|
787
|
+
|
|
788
|
+
from ..core.state import get_db_connection
|
|
789
|
+
from ..features.complexity.engine import get_complexity_summary
|
|
790
|
+
|
|
791
|
+
conn = get_db_connection()
|
|
792
|
+
if conn is None:
|
|
793
|
+
raise HTTPException(status_code=503, detail="database unavailable")
|
|
794
|
+
return await asyncio.to_thread(
|
|
795
|
+
get_complexity_summary,
|
|
796
|
+
conn,
|
|
797
|
+
recompute=recompute,
|
|
798
|
+
weeks=weeks,
|
|
799
|
+
window_days=window_days,
|
|
800
|
+
half_life_days=half_life_days,
|
|
801
|
+
)
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
@router.get("/complexity/timeline")
|
|
805
|
+
async def complexity_timeline(
|
|
806
|
+
_api_key: str = Depends(require_api_key),
|
|
807
|
+
recompute: bool = Query(False),
|
|
808
|
+
weeks: int = Query(12, ge=1, le=104),
|
|
809
|
+
):
|
|
810
|
+
import asyncio
|
|
811
|
+
|
|
812
|
+
from ..core.state import get_db_connection
|
|
813
|
+
from ..features.complexity.engine import get_shift_timeline
|
|
814
|
+
|
|
815
|
+
conn = get_db_connection()
|
|
816
|
+
if conn is None:
|
|
817
|
+
raise HTTPException(status_code=503, detail="database unavailable")
|
|
818
|
+
return await asyncio.to_thread(get_shift_timeline, conn, recompute=recompute, weeks=weeks)
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
@router.get("/complexity/topics/daily")
|
|
822
|
+
async def complexity_topics_daily(
|
|
823
|
+
_api_key: str = Depends(require_api_key),
|
|
824
|
+
days: int = Query(90, ge=1, le=365),
|
|
825
|
+
top: int = Query(10, ge=1, le=24),
|
|
826
|
+
):
|
|
827
|
+
"""Per-day supertopic shares — the braid's substrate (PLAN_TIMELINE_UNIFIED.md E2)."""
|
|
828
|
+
import asyncio
|
|
829
|
+
|
|
830
|
+
from ..core.state import get_db_connection
|
|
831
|
+
from ..features.complexity.topics_daily import topics_daily
|
|
832
|
+
|
|
833
|
+
conn = get_db_connection()
|
|
834
|
+
if conn is None:
|
|
835
|
+
raise HTTPException(status_code=503, detail="database unavailable")
|
|
836
|
+
return await asyncio.to_thread(topics_daily, conn, days=days, top=top)
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
@router.get("/complexity/influence")
|
|
840
|
+
async def complexity_influence(
|
|
841
|
+
_api_key: str = Depends(require_api_key),
|
|
842
|
+
recompute: bool = Query(False),
|
|
843
|
+
weeks: int = Query(12, ge=1, le=104),
|
|
844
|
+
top_k: int = Query(5, ge=1, le=50),
|
|
845
|
+
window_days: int = Query(90, ge=1, le=3650),
|
|
846
|
+
target: Optional[str] = Query(None, max_length=500),
|
|
847
|
+
):
|
|
848
|
+
import asyncio
|
|
849
|
+
|
|
850
|
+
from ..core.state import get_db_connection
|
|
851
|
+
from ..features.complexity.engine import get_influence_threads
|
|
852
|
+
|
|
853
|
+
conn = get_db_connection()
|
|
854
|
+
if conn is None:
|
|
855
|
+
raise HTTPException(status_code=503, detail="database unavailable")
|
|
856
|
+
return await asyncio.to_thread(
|
|
857
|
+
get_influence_threads,
|
|
858
|
+
conn,
|
|
859
|
+
recompute=recompute,
|
|
860
|
+
weeks=weeks,
|
|
861
|
+
top_k=top_k,
|
|
862
|
+
window_days=window_days,
|
|
863
|
+
target=target,
|
|
864
|
+
)
|
|
@@ -253,8 +253,12 @@ async def get_uma_messages(
|
|
|
253
253
|
)
|
|
254
254
|
items = _get_messages_from_db(conn, effective_dataset_id, limited, offset, allowed_tables=allowed_tables)
|
|
255
255
|
try:
|
|
256
|
+
# UMA data proxy = the grantee HTTP lane; filter unconditionally.
|
|
257
|
+
from ..features.lifecycle.blackhole_guard import BlackholeGuard, CallerClass
|
|
258
|
+
|
|
256
259
|
items, uma_contact_sidecar = apply_message_contact_pipeline(
|
|
257
260
|
items,
|
|
261
|
+
blackhole_guard=BlackholeGuard(conn, caller_class=CallerClass.GRANTEE),
|
|
258
262
|
conn=conn,
|
|
259
263
|
dataset_id=effective_dataset_id,
|
|
260
264
|
allowed_scopes=allowed_scopes,
|
|
@@ -48,6 +48,7 @@ from .api import (
|
|
|
48
48
|
home_chat as home_chat_routes,
|
|
49
49
|
privacy_disclose as privacy_disclose_routes,
|
|
50
50
|
signal as signal_routes,
|
|
51
|
+
shell as shell_routes,
|
|
51
52
|
tool_index as tool_index_routes,
|
|
52
53
|
upgrades as upgrades_routes,
|
|
53
54
|
)
|
|
@@ -100,6 +101,7 @@ app.add_middleware(
|
|
|
100
101
|
)
|
|
101
102
|
|
|
102
103
|
app.include_router(health_routes.router)
|
|
104
|
+
app.include_router(shell_routes.router)
|
|
103
105
|
app.include_router(upgrades_routes.router)
|
|
104
106
|
app.include_router(local_mcp_routes.router)
|
|
105
107
|
app.include_router(llm_routes.router)
|
|
Binary file
|
|
Binary file
|
|
@@ -132,6 +132,29 @@ def _emit_startup_banner(host: str, port: int, package_name: str = DEFAULT_PACKA
|
|
|
132
132
|
os.environ["TOPOS_STARTUP_BANNER_EMITTED"] = "1"
|
|
133
133
|
|
|
134
134
|
|
|
135
|
+
def _probe_running_node(host: str, port: int) -> dict | None:
|
|
136
|
+
"""Return the running node's shell status (or minimal version dict) if one
|
|
137
|
+
is already serving this port; None when the port is free or not a topos node."""
|
|
138
|
+
import httpx
|
|
139
|
+
|
|
140
|
+
probe_host = "127.0.0.1" if host in ("0.0.0.0", "::") else host
|
|
141
|
+
base = f"http://{probe_host}:{port}"
|
|
142
|
+
try:
|
|
143
|
+
response = httpx.get(f"{base}/v1/shell/status", timeout=2.0)
|
|
144
|
+
if response.status_code == 200 and response.json().get("name") == "topos-node":
|
|
145
|
+
return response.json()
|
|
146
|
+
except Exception:
|
|
147
|
+
return None
|
|
148
|
+
try:
|
|
149
|
+
# Older node without the shell endpoint.
|
|
150
|
+
response = httpx.get(f"{base}/version", timeout=2.0)
|
|
151
|
+
if response.status_code == 200 and "version" in response.json():
|
|
152
|
+
return {"version": response.json()["version"]}
|
|
153
|
+
except Exception:
|
|
154
|
+
return None
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
|
|
135
158
|
def _maybe_offer_self_update(
|
|
136
159
|
skip_update_check: bool,
|
|
137
160
|
package_name: str = DEFAULT_PACKAGE_NAME,
|
|
@@ -194,7 +217,19 @@ def _maybe_offer_self_update(
|
|
|
194
217
|
is_flag=True,
|
|
195
218
|
help="Skip checking PyPI for a newer topos-node version.",
|
|
196
219
|
)
|
|
197
|
-
|
|
220
|
+
@click.option(
|
|
221
|
+
"--tray/--no-tray",
|
|
222
|
+
"tray",
|
|
223
|
+
default=None,
|
|
224
|
+
help="Show a menu-bar/system-tray status icon (default: auto-detect GUI).",
|
|
225
|
+
)
|
|
226
|
+
@click.option(
|
|
227
|
+
"--app",
|
|
228
|
+
"app_mode",
|
|
229
|
+
is_flag=True,
|
|
230
|
+
help="App mode: tray icon only, logs to ~/.topos/logs/node.log instead of the terminal.",
|
|
231
|
+
)
|
|
232
|
+
def main(db_path, topos_key, set_topos_key, discover, port, host, skip_update_check, tray, app_mode) -> None:
|
|
198
233
|
"""Topos Control Plane API entry point."""
|
|
199
234
|
if set_topos_key:
|
|
200
235
|
env_path = _save_topos_key(set_topos_key)
|
|
@@ -221,8 +256,45 @@ def main(db_path, topos_key, set_topos_key, discover, port, host, skip_update_ch
|
|
|
221
256
|
_load_env_file(USER_ENV_PATH)
|
|
222
257
|
_load_env_file(LEGACY_ENV_PATH)
|
|
223
258
|
|
|
259
|
+
if app_mode and tray is None:
|
|
260
|
+
tray = True
|
|
261
|
+
|
|
262
|
+
# Attach, don't double-start: two nodes on one ~/.topos DB is a corruption
|
|
263
|
+
# risk, and for launchers "make sure Topos is running" should be idempotent.
|
|
264
|
+
running = _probe_running_node(host, port)
|
|
265
|
+
if running is not None:
|
|
266
|
+
running_version = running.get("version", "unknown")
|
|
267
|
+
from topos.cli import tray as tray_module
|
|
268
|
+
|
|
269
|
+
if tray_module.should_enable_tray(cli_flag=tray):
|
|
270
|
+
click.echo(
|
|
271
|
+
f"Topos Node v{running_version} is already running on port {port} — "
|
|
272
|
+
"attaching tray to it (closing this tray leaves the node running)."
|
|
273
|
+
)
|
|
274
|
+
log_file = running.get("log_file")
|
|
275
|
+
tray_module.attach_tray(
|
|
276
|
+
host=host,
|
|
277
|
+
port=port,
|
|
278
|
+
version=running_version,
|
|
279
|
+
package_name=DEFAULT_PACKAGE_NAME,
|
|
280
|
+
log_path=Path(log_file) if log_file else None,
|
|
281
|
+
)
|
|
282
|
+
return
|
|
283
|
+
raise click.ClickException(
|
|
284
|
+
f"Topos Node v{running_version} is already running on port {port}. "
|
|
285
|
+
"Use --port to start a second instance."
|
|
286
|
+
)
|
|
287
|
+
|
|
224
288
|
_resolve_topos_key(topos_key)
|
|
225
289
|
|
|
290
|
+
if app_mode:
|
|
291
|
+
# Must happen before `topos.app` is imported — configure_logging() runs
|
|
292
|
+
# at import time and reads TOPOS_LOG_FILE to pick file vs stdout.
|
|
293
|
+
from topos.core.logging import default_node_log_path
|
|
294
|
+
|
|
295
|
+
os.environ.setdefault("TOPOS_LOG_FILE", str(default_node_log_path()))
|
|
296
|
+
click.echo(f"App mode: logs → {os.environ['TOPOS_LOG_FILE']}")
|
|
297
|
+
|
|
226
298
|
from topos.app import app
|
|
227
299
|
from topos.core.logging import get_uvicorn_log_config
|
|
228
300
|
|
|
@@ -235,7 +307,23 @@ def main(db_path, topos_key, set_topos_key, discover, port, host, skip_update_ch
|
|
|
235
307
|
start_background_housekeeping()
|
|
236
308
|
|
|
237
309
|
_emit_startup_banner(host=host, port=port)
|
|
238
|
-
|
|
310
|
+
|
|
311
|
+
from topos.cli import tray as tray_module
|
|
312
|
+
|
|
313
|
+
if tray_module.should_enable_tray(cli_flag=tray):
|
|
314
|
+
from topos.core.logging import get_log_file_path
|
|
315
|
+
|
|
316
|
+
tray_module.serve_with_tray(
|
|
317
|
+
app,
|
|
318
|
+
host=host,
|
|
319
|
+
port=port,
|
|
320
|
+
log_config=get_uvicorn_log_config(),
|
|
321
|
+
version=_get_runtime_version(),
|
|
322
|
+
package_name=DEFAULT_PACKAGE_NAME,
|
|
323
|
+
log_path=get_log_file_path(),
|
|
324
|
+
)
|
|
325
|
+
else:
|
|
326
|
+
uvicorn.run(app, host=host, port=port, log_config=get_uvicorn_log_config())
|
|
239
327
|
|
|
240
328
|
|
|
241
329
|
if __name__ == "__main__":
|