topos-node 1.0.7__tar.gz → 1.0.8__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.
Files changed (472) hide show
  1. {topos_node-1.0.7 → topos_node-1.0.8}/PKG-INFO +1 -1
  2. {topos_node-1.0.7 → topos_node-1.0.8}/pyproject.toml +10 -1
  3. topos_node-1.0.8/tests/test_protocol_handled_types.py +70 -0
  4. {topos_node-1.0.7 → topos_node-1.0.8}/topos/__version__.py +2 -2
  5. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/ingestion_sources.py +9 -8
  6. topos_node-1.0.8/topos/api/intelligence_lifecycle.py +98 -0
  7. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/signal.py +182 -0
  8. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/source_install.py +11 -2
  9. {topos_node-1.0.7 → topos_node-1.0.8}/topos/app.py +2 -0
  10. {topos_node-1.0.7 → topos_node-1.0.8}/topos/config/settings.py +4 -0
  11. {topos_node-1.0.7 → topos_node-1.0.8}/topos/control_plane_client.py +8 -0
  12. topos_node-1.0.8/topos/core/handlers/__init__.py +178 -0
  13. topos_node-1.0.8/topos/core/handlers/common.py +162 -0
  14. topos_node-1.0.8/topos/core/handlers/config.py +346 -0
  15. topos_node-1.0.8/topos/core/handlers/contacts_import.py +287 -0
  16. topos_node-1.0.8/topos/core/handlers/database_explorer.py +1686 -0
  17. topos_node-1.0.8/topos/core/handlers/device.py +327 -0
  18. topos_node-1.0.8/topos/core/handlers/enrichment.py +465 -0
  19. topos_node-1.0.8/topos/core/handlers/filter_lab.py +230 -0
  20. topos_node-1.0.8/topos/core/handlers/home_chat.py +91 -0
  21. topos_node-1.0.8/topos/core/handlers/ingest.py +526 -0
  22. topos_node-1.0.8/topos/core/handlers/messages.py +1106 -0
  23. topos_node-1.0.8/topos/core/handlers/messenger_analytics.py +471 -0
  24. topos_node-1.0.8/topos/core/handlers/query.py +64 -0
  25. topos_node-1.0.8/topos/core/handlers/registry.py +21 -0
  26. topos_node-1.0.8/topos/core/handlers/routines.py +298 -0
  27. topos_node-1.0.8/topos/core/handlers/signal_features.py +628 -0
  28. topos_node-1.0.8/topos/core/handlers/sources.py +457 -0
  29. topos_node-1.0.8/topos/core/handlers/uma.py +753 -0
  30. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/logging.py +2 -3
  31. {topos_node-1.0.7 → topos_node-1.0.8}/topos/disclosure/field_registry.py +11 -0
  32. {topos_node-1.0.7 → topos_node-1.0.8}/topos/disclosure/tier.py +44 -12
  33. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/backends/huggingface.py +147 -4
  34. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/model_cache.py +3 -0
  35. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/__init__.py +12 -0
  36. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/embeddings_job.py +20 -9
  37. topos_node-1.0.8/topos/enrichment/jobs/canonical/entities_job.py +205 -0
  38. topos_node-1.0.8/topos/enrichment/jobs/canonical/fact_extraction_job.py +46 -0
  39. topos_node-1.0.8/topos/enrichment/jobs/canonical/statistics_job.py +116 -0
  40. topos_node-1.0.8/topos/enrichment/jobs/canonical/timeline_job.py +67 -0
  41. topos_node-1.0.8/topos/enrichment/jobs/canonical/topic_clusters_job.py +195 -0
  42. topos_node-1.0.8/topos/features/entities/__init__.py +1 -0
  43. topos_node-1.0.8/topos/features/entities/consolidation.py +349 -0
  44. topos_node-1.0.8/topos/features/entities/dossier.py +164 -0
  45. topos_node-1.0.8/topos/features/entities/edges.py +180 -0
  46. topos_node-1.0.8/topos/features/entities/linking.py +144 -0
  47. topos_node-1.0.8/topos/features/entities/reads.py +142 -0
  48. topos_node-1.0.8/topos/features/entities/resolver.py +486 -0
  49. topos_node-1.0.8/topos/features/facts/__init__.py +1 -0
  50. topos_node-1.0.8/topos/features/facts/extract.py +204 -0
  51. topos_node-1.0.8/topos/features/facts/reads.py +236 -0
  52. topos_node-1.0.8/topos/features/facts/store.py +346 -0
  53. topos_node-1.0.8/topos/features/lifecycle/__init__.py +1 -0
  54. topos_node-1.0.8/topos/features/lifecycle/derived_scrub.py +428 -0
  55. topos_node-1.0.8/topos/features/lifecycle/exclusions.py +239 -0
  56. topos_node-1.0.8/topos/features/signal/embed_context.py +110 -0
  57. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/hybrid_search.py +2 -1
  58. topos_node-1.0.8/topos/features/signal/incremental_clustering.py +304 -0
  59. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/service.py +65 -12
  60. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/topic_clustering.py +90 -25
  61. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/vector_settings.py +37 -0
  62. topos_node-1.0.8/topos/features/stats/__init__.py +1 -0
  63. topos_node-1.0.8/topos/features/stats/definitions.py +227 -0
  64. topos_node-1.0.8/topos/features/stats/engine.py +280 -0
  65. topos_node-1.0.8/topos/features/stats/fold.py +226 -0
  66. topos_node-1.0.8/topos/features/stats/insights.py +119 -0
  67. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/ingest_helpers.py +6 -4
  68. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/manager.py +4 -2
  69. topos_node-1.0.8/topos/query/ddr.py +135 -0
  70. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/disclosure.py +64 -1
  71. topos_node-1.0.8/topos/query/fingerprint.py +35 -0
  72. topos_node-1.0.8/topos/query/grant_cache.py +159 -0
  73. topos_node-1.0.8/topos/query/minimizer.py +315 -0
  74. topos_node-1.0.8/topos/query/negotiation.py +384 -0
  75. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/pipeline.py +150 -3
  76. topos_node-1.0.8/topos/query/planner.py +175 -0
  77. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/retrieval.py +400 -88
  78. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/session.py +3 -0
  79. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/turn_classifier.py +6 -0
  80. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sources/canonical_signal_defaults.py +3 -0
  81. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sources/definitions.py +76 -0
  82. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sources/install_service.py +8 -0
  83. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sources/registry.py +16 -0
  84. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sources/runtime_install.py +2 -1
  85. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sources/scrub_service.py +48 -0
  86. topos_node-1.0.8/topos/storage/adapters/sqlite/__init__.py +0 -0
  87. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/adapters/sqlite/stores.py +8 -8
  88. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/adapters/sqlite/vector_search.py +49 -27
  89. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/__init__.py +32 -0
  90. topos_node-1.0.8/topos/storage/db/migrations/vector_storage_v4.py +147 -0
  91. topos_node-1.0.8/topos/storage/db/migrations/wiki_clusters_v2.py +44 -0
  92. topos_node-1.0.8/topos/storage/db/migrations/wiki_entities_v1.py +99 -0
  93. topos_node-1.0.8/topos/storage/db/migrations/wiki_entity_review_v2.py +38 -0
  94. topos_node-1.0.8/topos/storage/db/migrations/wiki_facts_v1.py +41 -0
  95. topos_node-1.0.8/topos/storage/db/migrations/wiki_lifecycle_v1.py +41 -0
  96. topos_node-1.0.8/topos/storage/db/migrations/wiki_stats_v1.py +66 -0
  97. topos_node-1.0.8/topos/storage/db/migrations/wiki_timeline_v1.py +39 -0
  98. {topos_node-1.0.7 → topos_node-1.0.8}/topos_node.egg-info/PKG-INFO +1 -1
  99. {topos_node-1.0.7 → topos_node-1.0.8}/topos_node.egg-info/SOURCES.txt +58 -1
  100. topos_node-1.0.7/topos/core/handlers.py +0 -6941
  101. topos_node-1.0.7/topos/enrichment/jobs/canonical/entities_job.py +0 -69
  102. topos_node-1.0.7/topos/enrichment/jobs/canonical/topic_clusters_job.py +0 -84
  103. topos_node-1.0.7/topos/query/fingerprint.py +0 -21
  104. {topos_node-1.0.7 → topos_node-1.0.8}/LICENSE +0 -0
  105. {topos_node-1.0.7 → topos_node-1.0.8}/README.md +0 -0
  106. {topos_node-1.0.7 → topos_node-1.0.8}/setup.cfg +0 -0
  107. {topos_node-1.0.7 → topos_node-1.0.8}/shared/__init__.py +0 -0
  108. {topos_node-1.0.7 → topos_node-1.0.8}/shared/filtering.py +0 -0
  109. {topos_node-1.0.7 → topos_node-1.0.8}/shared/schema_registry.py +0 -0
  110. {topos_node-1.0.7 → topos_node-1.0.8}/tests/test_llm_integrations_storage.py +0 -0
  111. {topos_node-1.0.7 → topos_node-1.0.8}/topos/__init__.py +0 -0
  112. {topos_node-1.0.7 → topos_node-1.0.8}/topos/analytics/__init__.py +0 -0
  113. {topos_node-1.0.7 → topos_node-1.0.8}/topos/analytics/duckdb_adapter.py +0 -0
  114. {topos_node-1.0.7 → topos_node-1.0.8}/topos/analytics/messenger_communities.py +0 -0
  115. {topos_node-1.0.7 → topos_node-1.0.8}/topos/analytics/messenger_graph.py +0 -0
  116. {topos_node-1.0.7 → topos_node-1.0.8}/topos/analytics/messenger_labels.py +0 -0
  117. {topos_node-1.0.7 → topos_node-1.0.8}/topos/analytics/profiles.py +0 -0
  118. {topos_node-1.0.7 → topos_node-1.0.8}/topos/analytics/query_engine.py +0 -0
  119. {topos_node-1.0.7 → topos_node-1.0.8}/topos/analytics/raw_queries.py +0 -0
  120. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/__init__.py +0 -0
  121. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/analytics.py +0 -0
  122. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/app_registry.py +0 -0
  123. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/backup.py +0 -0
  124. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/compute_remote.py +0 -0
  125. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/data_commit.py +0 -0
  126. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/data_explorer_table_prefs.py +0 -0
  127. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/database_explorer.py +0 -0
  128. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/db.py +0 -0
  129. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/device.py +0 -0
  130. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/engine_run.py +0 -0
  131. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/enrichment.py +0 -0
  132. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/filter_lab.py +0 -0
  133. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/health.py +0 -0
  134. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/home_chat.py +0 -0
  135. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/ingestion_api.py +0 -0
  136. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/ingestion_compat.py +0 -0
  137. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/llm.py +0 -0
  138. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/local_mcp.py +0 -0
  139. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/messenger_analytics.py +0 -0
  140. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/privacy_disclose.py +0 -0
  141. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/query_api.py +0 -0
  142. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/runtime_bootstrap.py +0 -0
  143. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/sanitization_ollama_config.py +0 -0
  144. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/signal_extraction_config.py +0 -0
  145. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/source_scrub.py +0 -0
  146. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/sources.py +0 -0
  147. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/sources_overview.py +0 -0
  148. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/sync.py +0 -0
  149. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/ui_config.py +0 -0
  150. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/uma_data.py +0 -0
  151. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/usage.py +0 -0
  152. {topos_node-1.0.7 → topos_node-1.0.8}/topos/api/user_identity.py +0 -0
  153. {topos_node-1.0.7 → topos_node-1.0.8}/topos/auth.py +0 -0
  154. {topos_node-1.0.7 → topos_node-1.0.8}/topos/canonicalization/__init__.py +0 -0
  155. {topos_node-1.0.7 → topos_node-1.0.8}/topos/canonicalization/mappers/__init__.py +0 -0
  156. {topos_node-1.0.7 → topos_node-1.0.8}/topos/canonicalization/mappers/base.py +0 -0
  157. {topos_node-1.0.7 → topos_node-1.0.8}/topos/canonicalization/mappers/browser_activity_mapper.py +0 -0
  158. {topos_node-1.0.7 → topos_node-1.0.8}/topos/canonicalization/mappers/chatgpt_mapper.py +0 -0
  159. {topos_node-1.0.7 → topos_node-1.0.8}/topos/canonicalization/mappers/demo_mappers.py +0 -0
  160. {topos_node-1.0.7 → topos_node-1.0.8}/topos/canonicalization/mappers/grok_mapper.py +0 -0
  161. {topos_node-1.0.7 → topos_node-1.0.8}/topos/canonicalization/mappers/messenger_mapper.py +0 -0
  162. {topos_node-1.0.7 → topos_node-1.0.8}/topos/canonicalization/models.py +0 -0
  163. {topos_node-1.0.7 → topos_node-1.0.8}/topos/canonicalization/resolver.py +0 -0
  164. {topos_node-1.0.7 → topos_node-1.0.8}/topos/cli/__init__.py +0 -0
  165. {topos_node-1.0.7 → topos_node-1.0.8}/topos/cli/__main__.py +0 -0
  166. {topos_node-1.0.7 → topos_node-1.0.8}/topos/cli/commands.py +0 -0
  167. {topos_node-1.0.7 → topos_node-1.0.8}/topos/config/__init__.py +0 -0
  168. {topos_node-1.0.7 → topos_node-1.0.8}/topos/config/sanitization_ollama.py +0 -0
  169. {topos_node-1.0.7 → topos_node-1.0.8}/topos/config/signal_extraction.py +0 -0
  170. {topos_node-1.0.7 → topos_node-1.0.8}/topos/contacts/__init__.py +0 -0
  171. {topos_node-1.0.7 → topos_node-1.0.8}/topos/contacts/identity.py +0 -0
  172. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/__init__.py +0 -0
  173. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/api_models.py +0 -0
  174. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/connection_resilience.py +0 -0
  175. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/device_helpers.py +0 -0
  176. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/errors.py +0 -0
  177. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/events.py +0 -0
  178. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/metrics.py +0 -0
  179. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/routine_access.py +0 -0
  180. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/startup_banner.py +0 -0
  181. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/state.py +0 -0
  182. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/table_layers.py +0 -0
  183. {topos_node-1.0.7 → topos_node-1.0.8}/topos/core/types.py +0 -0
  184. {topos_node-1.0.7 → topos_node-1.0.8}/topos/data_explorer_row_delete.py +0 -0
  185. {topos_node-1.0.7 → topos_node-1.0.8}/topos/data_explorer_table_prefs.py +0 -0
  186. {topos_node-1.0.7 → topos_node-1.0.8}/topos/data_explorer_tables.py +0 -0
  187. {topos_node-1.0.7 → topos_node-1.0.8}/topos/disclosure/__init__.py +0 -0
  188. {topos_node-1.0.7 → topos_node-1.0.8}/topos/disclosure/canonical_writer.py +0 -0
  189. {topos_node-1.0.7 → topos_node-1.0.8}/topos/disclosure/content_policy.py +0 -0
  190. {topos_node-1.0.7 → topos_node-1.0.8}/topos/disclosure/privacy_layer.py +0 -0
  191. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/__init__.py +0 -0
  192. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/backends/__init__.py +0 -0
  193. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/backends/base.py +0 -0
  194. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/backends/generative_prompts.py +0 -0
  195. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/backends/generative_response.py +0 -0
  196. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/backends/ollama.py +0 -0
  197. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/backends/openai.py +0 -0
  198. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/backends/openai_compatible.py +0 -0
  199. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/backends/redpill.py +0 -0
  200. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/backends/stub.py +0 -0
  201. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/client.py +0 -0
  202. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/engine.py +0 -0
  203. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/intake.py +0 -0
  204. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/memory_utils.py +0 -0
  205. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/pipeline_memory.py +0 -0
  206. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/queue_manager.py +0 -0
  207. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/registration.py +0 -0
  208. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/result_formatter.py +0 -0
  209. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/router.py +0 -0
  210. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/scoped_token.py +0 -0
  211. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/tasks.py +0 -0
  212. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/transport.py +0 -0
  213. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/usage_guard.py +0 -0
  214. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/usage_observation.py +0 -0
  215. {topos_node-1.0.7 → topos_node-1.0.8}/topos/engine/validator.py +0 -0
  216. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/__init__.py +0 -0
  217. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/derived_tables.py +0 -0
  218. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/job_writer.py +0 -0
  219. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/base.py +0 -0
  220. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/__init__.py +0 -0
  221. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/_batch_limits.py +0 -0
  222. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/_engine_runner.py +0 -0
  223. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/availability_scores_job.py +0 -0
  224. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/brief_fallback.py +0 -0
  225. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/dimension_summary_job.py +0 -0
  226. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/emo_27_job.py +0 -0
  227. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/goal_extraction_job.py +0 -0
  228. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/pii_redaction_job.py +0 -0
  229. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/relationship_edges_job.py +0 -0
  230. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/sentiment_job.py +0 -0
  231. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/topics_job.py +0 -0
  232. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/url_classification_core.py +0 -0
  233. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/url_classification_enrichment_job.py +0 -0
  234. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/canonical/url_classification_signal_job.py +0 -0
  235. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/raw/__init__.py +0 -0
  236. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/raw/attachments_job.py +0 -0
  237. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/raw/language_job.py +0 -0
  238. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/raw/time_normalization_job.py +0 -0
  239. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/jobs/raw/tool_calls_job.py +0 -0
  240. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/models/__init__.py +0 -0
  241. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/models/manager.py +0 -0
  242. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/models/mvp_defaults.py +0 -0
  243. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/models/registry.py +0 -0
  244. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/models/versioning.py +0 -0
  245. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/orchestrator.py +0 -0
  246. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/processor.py +0 -0
  247. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/progress_bar.py +0 -0
  248. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/registry_contract.py +0 -0
  249. {topos_node-1.0.7 → topos_node-1.0.8}/topos/enrichment/website_classifier.py +0 -0
  250. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/fit/evaluator.py +0 -0
  251. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/__init__.py +0 -0
  252. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/brief_canonical_loader.py +0 -0
  253. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/brief_ontology.py +0 -0
  254. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/brief_renderer.py +0 -0
  255. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/brief_schemas.py +0 -0
  256. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/chunking.py +0 -0
  257. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/data_health.py +0 -0
  258. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/dimension_briefs.py +0 -0
  259. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/dimension_definition_loader.py +0 -0
  260. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/dimension_profiles.py +0 -0
  261. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/dimension_registry.py +0 -0
  262. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/extraction/artifact_router.py +0 -0
  263. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/extraction/artifact_store.py +0 -0
  264. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/extraction/rule_extractors.py +0 -0
  265. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/graph_sanitize.py +0 -0
  266. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/query_embed_cache.py +0 -0
  267. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/schemas.py +0 -0
  268. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/signal_object_store.py +0 -0
  269. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/source_hydration.py +0 -0
  270. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/structured_signal.py +0 -0
  271. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/typed_stores/aggregates.py +0 -0
  272. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/typed_stores/scope_materializer.py +0 -0
  273. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/vector_codec.py +0 -0
  274. {topos_node-1.0.7 → topos_node-1.0.8}/topos/features/signal/vector_math.py +0 -0
  275. {topos_node-1.0.7 → topos_node-1.0.8}/topos/filter_lab/__init__.py +0 -0
  276. {topos_node-1.0.7 → topos_node-1.0.8}/topos/filter_lab/bundles.py +0 -0
  277. {topos_node-1.0.7 → topos_node-1.0.8}/topos/filter_lab/schema.py +0 -0
  278. {topos_node-1.0.7 → topos_node-1.0.8}/topos/filter_lab/service.py +0 -0
  279. {topos_node-1.0.7 → topos_node-1.0.8}/topos/filter_lab/store.py +0 -0
  280. {topos_node-1.0.7 → topos_node-1.0.8}/topos/filter_lab/worker.py +0 -0
  281. {topos_node-1.0.7 → topos_node-1.0.8}/topos/home_chat/__init__.py +0 -0
  282. {topos_node-1.0.7 → topos_node-1.0.8}/topos/home_chat/schema.py +0 -0
  283. {topos_node-1.0.7 → topos_node-1.0.8}/topos/home_chat/store.py +0 -0
  284. {topos_node-1.0.7 → topos_node-1.0.8}/topos/hosted_pool_lease.py +0 -0
  285. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/__init__.py +0 -0
  286. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/canonical_pipeline.py +0 -0
  287. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/checkpoints/__init__.py +0 -0
  288. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/checkpoints/checkpoint_store.py +0 -0
  289. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/checkpoints/sqlite_checkpoint_store.py +0 -0
  290. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/inbox_crypto.py +0 -0
  291. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/inbox_drain.py +0 -0
  292. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/jobs.py +0 -0
  293. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/journal_location_fanout.py +0 -0
  294. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/journal_time_log_normalize.py +0 -0
  295. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/local_sync.py +0 -0
  296. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/log_preview.py +0 -0
  297. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/parser.py +0 -0
  298. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/parsers/__init__.py +0 -0
  299. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/parsers/base.py +0 -0
  300. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/parsers/browser_parser.py +0 -0
  301. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/parsers/calendar_parser.py +0 -0
  302. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/parsers/chatgpt_conversation_flattener.py +0 -0
  303. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/parsers/chatgpt_parser.py +0 -0
  304. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/parsers/demo_file_parsers.py +0 -0
  305. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/parsers/grok_parser.py +0 -0
  306. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/parsers/messenger_parser.py +0 -0
  307. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/parsers/voxterm_parser.py +0 -0
  308. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/progress.py +0 -0
  309. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/reprocess.py +0 -0
  310. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/sources/__init__.py +0 -0
  311. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/sources/base.py +0 -0
  312. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/sources/calendar.py +0 -0
  313. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/sources/chatgpt.py +0 -0
  314. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/sources/contact_importers.py +0 -0
  315. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/sources/grok.py +0 -0
  316. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/sources/imessage_reader.py +0 -0
  317. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/sources/signal_export_parser.py +0 -0
  318. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/sources/signal_reader.py +0 -0
  319. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/state_machine.py +0 -0
  320. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/triggers/__init__.py +0 -0
  321. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/triggers/file_trigger.py +0 -0
  322. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/triggers/sqlite_trigger.py +0 -0
  323. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/usage_inbox_dedupe.py +0 -0
  324. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/validation/__init__.py +0 -0
  325. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/validation/base.py +0 -0
  326. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/validation/schema_registry.py +0 -0
  327. {topos_node-1.0.7 → topos_node-1.0.8}/topos/ingestion/validation/schema_validator.py +0 -0
  328. {topos_node-1.0.7 → topos_node-1.0.8}/topos/lineage/__init__.py +0 -0
  329. {topos_node-1.0.7 → topos_node-1.0.8}/topos/lineage/provenance.py +0 -0
  330. {topos_node-1.0.7 → topos_node-1.0.8}/topos/lineage/tracker.py +0 -0
  331. {topos_node-1.0.7 → topos_node-1.0.8}/topos/llm_integrations_storage.py +0 -0
  332. {topos_node-1.0.7 → topos_node-1.0.8}/topos/mcp_stdio_proxy.py +0 -0
  333. {topos_node-1.0.7 → topos_node-1.0.8}/topos/observability/__init__.py +0 -0
  334. {topos_node-1.0.7 → topos_node-1.0.8}/topos/observability/alerts.py +0 -0
  335. {topos_node-1.0.7 → topos_node-1.0.8}/topos/observability/metrics.py +0 -0
  336. {topos_node-1.0.7 → topos_node-1.0.8}/topos/observability/tracing.py +0 -0
  337. {topos_node-1.0.7 → topos_node-1.0.8}/topos/openai_client.py +0 -0
  338. {topos_node-1.0.7 → topos_node-1.0.8}/topos/pipeline/__init__.py +0 -0
  339. {topos_node-1.0.7 → topos_node-1.0.8}/topos/pipeline/audit.py +0 -0
  340. {topos_node-1.0.7 → topos_node-1.0.8}/topos/pipeline/envelope.py +0 -0
  341. {topos_node-1.0.7 → topos_node-1.0.8}/topos/pipeline/stages.py +0 -0
  342. {topos_node-1.0.7 → topos_node-1.0.8}/topos/pipeline/stub_enqueue.py +0 -0
  343. {topos_node-1.0.7 → topos_node-1.0.8}/topos/projections/__init__.py +0 -0
  344. {topos_node-1.0.7 → topos_node-1.0.8}/topos/projections/vector_index/__init__.py +0 -0
  345. {topos_node-1.0.7 → topos_node-1.0.8}/topos/projections/vector_index/base.py +0 -0
  346. {topos_node-1.0.7 → topos_node-1.0.8}/topos/projections/vector_index/builders.py +0 -0
  347. {topos_node-1.0.7 → topos_node-1.0.8}/topos/projections/vector_index/health_checks.py +0 -0
  348. {topos_node-1.0.7/topos/storage/adapters/sqlite → topos_node-1.0.8/topos/protocol}/__init__.py +0 -0
  349. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/__init__.py +0 -0
  350. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/audit.py +0 -0
  351. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/game_layer.py +0 -0
  352. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/inference.py +0 -0
  353. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/intent.py +0 -0
  354. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/manifest.py +0 -0
  355. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/manifest_validation.py +0 -0
  356. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/runtime.py +0 -0
  357. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/scope_registry_loader.py +0 -0
  358. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/session_utils.py +0 -0
  359. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/source_generation.py +0 -0
  360. {topos_node-1.0.7 → topos_node-1.0.8}/topos/query/types.py +0 -0
  361. {topos_node-1.0.7 → topos_node-1.0.8}/topos/rate_limit.py +0 -0
  362. {topos_node-1.0.7 → topos_node-1.0.8}/topos/routines/__init__.py +0 -0
  363. {topos_node-1.0.7 → topos_node-1.0.8}/topos/routines/schema.py +0 -0
  364. {topos_node-1.0.7 → topos_node-1.0.8}/topos/routines/store.py +0 -0
  365. {topos_node-1.0.7 → topos_node-1.0.8}/topos/runtime_update.py +0 -0
  366. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sanitization/__init__.py +0 -0
  367. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sanitization/nsfw_classifier.py +0 -0
  368. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sanitization/ollama_transforms.py +0 -0
  369. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sanitization/privacy_filter.py +0 -0
  370. {topos_node-1.0.7 → topos_node-1.0.8}/topos/scope_resolution.py +0 -0
  371. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/__init__.py +0 -0
  372. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/container.py +0 -0
  373. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/embeddings/__init__.py +0 -0
  374. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/embeddings/base.py +0 -0
  375. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/embeddings/local.py +0 -0
  376. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/embeddings/remote.py +0 -0
  377. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/interfaces.py +0 -0
  378. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/llm/__init__.py +0 -0
  379. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/llm/base.py +0 -0
  380. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/llm/openai.py +0 -0
  381. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/local.py +0 -0
  382. {topos_node-1.0.7 → topos_node-1.0.8}/topos/services/postgres.py +0 -0
  383. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sources/__init__.py +0 -0
  384. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sources/bundled_canonical_triples.py +0 -0
  385. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sources/scrub_attribution.py +0 -0
  386. {topos_node-1.0.7 → topos_node-1.0.8}/topos/startup_banner.py +0 -0
  387. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/__init__.py +0 -0
  388. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/adapters/__init__.py +0 -0
  389. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/adapters/factory.py +0 -0
  390. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/adapters/fakes.py +0 -0
  391. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/adapters/postgres/stores.py +0 -0
  392. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/adapters/protocols.py +0 -0
  393. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/__init__.py +0 -0
  394. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/activity_tables.py +0 -0
  395. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/ai_chat/__init__.py +0 -0
  396. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/ai_chat/canonicalizer.py +0 -0
  397. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/ai_chat/mapper.py +0 -0
  398. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/ai_chat/model.py +0 -0
  399. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/ai_chat/tables.py +0 -0
  400. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/calendar_contacts_tables.py +0 -0
  401. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/canonical_store.py +0 -0
  402. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/conversations_tables.py +0 -0
  403. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/mapping_store.py +0 -0
  404. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/canonical/postgres.py +0 -0
  405. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/__init__.py +0 -0
  406. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/client.py +0 -0
  407. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/canonical_disclosure_v1.py +0 -0
  408. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/canonical_nsfw_v1.py +0 -0
  409. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/extraction_artifacts.py +0 -0
  410. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/journal_entries_duration_v1.py +0 -0
  411. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/journal_entries_ends_at_v1.py +0 -0
  412. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/journal_entries_people_v1.py +0 -0
  413. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/journal_entries_place_name_v1.py +0 -0
  414. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/journal_entries_starts_at_v1.py +0 -0
  415. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/remediation_person_model.py +0 -0
  416. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/scope_source_generation.py +0 -0
  417. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/signal_dimension_harness.py +0 -0
  418. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/signal_objects.py +0 -0
  419. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/signal_objects_updated_by_v1.py +0 -0
  420. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/stage9_column_renames.py +0 -0
  421. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/vector_storage_v1.py +0 -0
  422. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/vector_storage_v2.py +0 -0
  423. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/vector_storage_v3.py +0 -0
  424. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/wiki_mvp_phase0.py +0 -0
  425. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/wiki_mvp_phase1.py +0 -0
  426. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/wiki_mvp_phase4_messages_cutover.py +0 -0
  427. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/wiki_mvp_phase5_topic_clusters.py +0 -0
  428. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/wiki_mvp_phase6_dimension_briefs.py +0 -0
  429. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/wiki_mvp_query_quality.py +0 -0
  430. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/migrations/wiki_mvp_topic_clusters_coordination.py +0 -0
  431. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/paths.py +0 -0
  432. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/postgres.py +0 -0
  433. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/schema.py +0 -0
  434. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/db/storage_breakdown.py +0 -0
  435. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/enrichment/__init__.py +0 -0
  436. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/enrichment/canonical_enrichment_store.py +0 -0
  437. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/enrichment/raw_enrichment_store.py +0 -0
  438. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/normalized/__init__.py +0 -0
  439. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/normalized/normalized_store.py +0 -0
  440. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/oplog/__init__.py +0 -0
  441. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/oplog/decision.py +0 -0
  442. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/oplog/oplog_store.py +0 -0
  443. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/oplog/postgres.py +0 -0
  444. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/person/identity_resolver.py +0 -0
  445. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/projections/__init__.py +0 -0
  446. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/projections/index_ops_store.py +0 -0
  447. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/projections/vector_index_store.py +0 -0
  448. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/raw/__init__.py +0 -0
  449. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/raw/browser_flat_tables.py +0 -0
  450. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/raw/file_store.py +0 -0
  451. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/raw/raw_store.py +0 -0
  452. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/raw/raw_tables_manager.py +0 -0
  453. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/raw/sqlite_raw_store.py +0 -0
  454. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/security/encryption.py +0 -0
  455. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/signal_identity.py +0 -0
  456. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/source_settings.py +0 -0
  457. {topos_node-1.0.7 → topos_node-1.0.8}/topos/storage/user_identity.py +0 -0
  458. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sync/__init__.py +0 -0
  459. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sync/client.py +0 -0
  460. {topos_node-1.0.7 → topos_node-1.0.8}/topos/sync_handlers.py +0 -0
  461. {topos_node-1.0.7 → topos_node-1.0.8}/topos/testing/__init__.py +0 -0
  462. {topos_node-1.0.7 → topos_node-1.0.8}/topos/testing/lifespan.py +0 -0
  463. {topos_node-1.0.7 → topos_node-1.0.8}/topos/uma_contact_enrichment.py +0 -0
  464. {topos_node-1.0.7 → topos_node-1.0.8}/topos/uma_filters.py +0 -0
  465. {topos_node-1.0.7 → topos_node-1.0.8}/topos/uma_resource_id.py +0 -0
  466. {topos_node-1.0.7 → topos_node-1.0.8}/topos/uma_rpt.py +0 -0
  467. {topos_node-1.0.7 → topos_node-1.0.8}/topos/utils/base_object.py +0 -0
  468. {topos_node-1.0.7 → topos_node-1.0.8}/topos/websocket_client.py +0 -0
  469. {topos_node-1.0.7 → topos_node-1.0.8}/topos_node.egg-info/dependency_links.txt +0 -0
  470. {topos_node-1.0.7 → topos_node-1.0.8}/topos_node.egg-info/entry_points.txt +0 -0
  471. {topos_node-1.0.7 → topos_node-1.0.8}/topos_node.egg-info/requires.txt +0 -0
  472. {topos_node-1.0.7 → topos_node-1.0.8}/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.0.7
3
+ Version: 1.0.8
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "topos-node"
7
- version = "1.0.7"
7
+ version = "1.0.8"
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"
@@ -89,6 +89,15 @@ markers = [
89
89
  "public: public-safe test lane for OSS CI",
90
90
  "private: internal architecture or fixtures; excluded from public CI",
91
91
  "e2e: end-to-end tests (live Keycloak + Control Plane; require config and running CP)",
92
+ "live: requires a running local engine (localhost:9000) and a real TOPOS_KEY; excluded from deterministic lanes",
93
+ "release_pressure: iteration-4 live pressure smoke; run explicitly before releases",
94
+ "p0: priority marker on release pressure tests",
95
+ "retrieval_eval: dense-intelligence retrieval eval (loads local embedding model; run explicitly)",
96
+ "adversarial: scope-break / access-control adversarial test (privacy enforcement)",
97
+ "uar: unauthorized-access-rate probe (privacy claim #1); tier-1 CI gate, must find zero leaks",
98
+ "cer: canary-extraction-rate probe (privacy claim #2); tier-1 CI gate",
99
+ "eval_nightly: statistical privacy eval (attacker advantage, minimality, latency); nightly",
100
+ "eval_release: release-tier privacy eval (negotiation A/B, Pareto); run per release",
92
101
  ]
93
102
  pythonpath = ["tests/gap/p3/engine", "tests/gap/p4/engine", "tests/gap/qq/engine", "tests/acceptance/wiki_mvp"]
94
103
  testpaths = ["tests"]
@@ -0,0 +1,70 @@
1
+ """Guards for the handled-message-types protocol snapshot.
2
+
3
+ The snapshot (topos/protocol/handled_message_types.json) is the machine-readable
4
+ list of control-plane message types this engine dispatches. Two invariants:
5
+
6
+ 1. Freshness — the committed snapshot equals the runtime HANDLERS registry.
7
+ Adding or removing an @handles registration requires regenerating it:
8
+ uv run python scripts/dump_handled_types.py > topos/protocol/handled_message_types.json
9
+
10
+ 2. Static parity — the set of string literals in @handles(...) decorators equals
11
+ the runtime registry, so no handler is registered dynamically in a way that
12
+ static consumers of the snapshot (or this repo's source) would miss.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import ast
17
+ import json
18
+ from pathlib import Path
19
+
20
+ REPO_ROOT = Path(__file__).resolve().parents[1]
21
+ SNAPSHOT = REPO_ROOT / "topos" / "protocol" / "handled_message_types.json"
22
+
23
+
24
+ def _runtime_handled() -> set[str]:
25
+ from topos.core.handlers import HANDLERS
26
+
27
+ return set(HANDLERS)
28
+
29
+
30
+ def _static_handled() -> set[str]:
31
+ found: set[str] = set()
32
+ for py in sorted((REPO_ROOT / "topos").rglob("*.py")):
33
+ if "__pycache__" in py.parts:
34
+ continue
35
+ try:
36
+ tree = ast.parse(py.read_text(encoding="utf-8"))
37
+ except SyntaxError:
38
+ continue
39
+ for node in ast.walk(tree):
40
+ if isinstance(node, ast.Call) and getattr(node.func, "id", "") == "handles":
41
+ for arg in node.args:
42
+ if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
43
+ found.add(arg.value)
44
+ return found
45
+
46
+
47
+ def test_snapshot_matches_runtime_registry():
48
+ snapshot = set(json.loads(SNAPSHOT.read_text(encoding="utf-8"))["handled_message_types"])
49
+ runtime = _runtime_handled()
50
+ missing = sorted(runtime - snapshot)
51
+ stale = sorted(snapshot - runtime)
52
+ assert not missing and not stale, (
53
+ "topos/protocol/handled_message_types.json is stale "
54
+ f"(missing={missing}, stale={stale}). Regenerate with: "
55
+ "uv run python scripts/dump_handled_types.py > topos/protocol/handled_message_types.json"
56
+ )
57
+
58
+
59
+ def test_static_handles_literals_match_runtime_registry():
60
+ runtime = _runtime_handled()
61
+ static = _static_handled()
62
+ dynamic_only = sorted(runtime - static)
63
+ unregistered = sorted(static - runtime)
64
+ assert not dynamic_only, (
65
+ "Handlers registered without a literal @handles(...) decorator; static "
66
+ f"consumers of the registry cannot see them: {dynamic_only}"
67
+ )
68
+ assert not unregistered, (
69
+ f"@handles(...) literals that never reach the runtime registry: {unregistered}"
70
+ )
@@ -1,6 +1,6 @@
1
1
  """Topos Control Plane Version Information."""
2
2
 
3
- __version__ = "1.0.7"
4
- __version_info__ = (1, 0, 7)
3
+ __version__ = "1.0.8"
4
+ __version_info__ = (1, 0, 8)
5
5
 
6
6
  __all__ = ["__version__", "__version_info__"]
@@ -12,6 +12,7 @@ from ..auth import require_api_key
12
12
  from ..core.state import get_db_connection
13
13
  from ..ingestion.ingest_helpers import ingest_file_payload, ingest_ui_payload, resolve_file_format
14
14
  from ..ingestion.local_sync import run_imessage_sync, run_signal_sync, run_signal_upload
15
+ from ..sources.definitions import DELIVERY_LOCAL_SYNC, accepts_app_ingest, is_file_delivery
15
16
  from ..sources.registry import REGISTRY
16
17
  from ..storage.signal_identity import get_signal_identity, put_signal_identity
17
18
  from ..storage.source_settings import get_source_settings, put_source_settings, update_sync_result
@@ -55,7 +56,7 @@ async def ingest_source(
55
56
  if not source:
56
57
  return {"status": "error", "error": "unknown source_id"}
57
58
 
58
- if source.source_type == "file":
59
+ if is_file_delivery(source):
59
60
  file = None
60
61
  if request.headers.get("content-type", "").startswith("multipart/"):
61
62
  form = await request.form()
@@ -108,7 +109,7 @@ async def ingest_source(
108
109
  file_format=resolve_file_format(source_definition=source),
109
110
  )
110
111
 
111
- if source.source_type == "ui_stream":
112
+ if accepts_app_ingest(source):
112
113
  if payload is None:
113
114
  try:
114
115
  payload = await request.json()
@@ -123,7 +124,7 @@ async def ingest_source(
123
124
  source_id=source_id, # Pass source_id to enable direct processing
124
125
  )
125
126
 
126
- if source.source_type == "local_sync":
127
+ if source.delivery == DELIVERY_LOCAL_SYNC:
127
128
  # iMessage, Signal: ingestion is via sync endpoint (POST /sources/{source_id}/sync), not file/body upload
128
129
  return {
129
130
  "status": "error",
@@ -161,7 +162,7 @@ async def put_source_settings_endpoint(
161
162
  source = REGISTRY.get(source_id)
162
163
  if not source:
163
164
  return {"status": "error", "error": "unknown source_id"}
164
- if source.source_type != "local_sync":
165
+ if source.delivery != DELIVERY_LOCAL_SYNC:
165
166
  return {"status": "error", "error": "settings only apply to local_sync sources"}
166
167
  dataset_id = dataset_id or (body.get("dataset_id") if body else None)
167
168
  if not dataset_id:
@@ -186,7 +187,7 @@ async def sync_source(
186
187
  source = REGISTRY.get(source_id)
187
188
  if not source:
188
189
  return {"status": "error", "error": "unknown source_id"}
189
- if source.source_type != "local_sync":
190
+ if source.delivery != DELIVERY_LOCAL_SYNC:
190
191
  return {"status": "error", "error": "sync endpoint only applies to local_sync sources"}
191
192
 
192
193
  if not dataset_id:
@@ -315,7 +316,7 @@ async def get_source_contacts(
315
316
  source = REGISTRY.get(source_id)
316
317
  if not source:
317
318
  return {"status": "error", "error": "unknown source_id"}
318
- if source.source_type != "local_sync":
319
+ if source.delivery != DELIVERY_LOCAL_SYNC:
319
320
  return {"status": "error", "error": "contacts only apply to local_sync sources"}
320
321
  if not dataset_id:
321
322
  return {"status": "error", "error": "dataset_id required"}
@@ -368,7 +369,7 @@ async def put_source_contact(
368
369
  source = REGISTRY.get(source_id)
369
370
  if not source:
370
371
  return {"status": "error", "error": "unknown source_id"}
371
- if source.source_type != "local_sync":
372
+ if source.delivery != DELIVERY_LOCAL_SYNC:
372
373
  return {"status": "error", "error": "contacts only apply to local_sync sources"}
373
374
  if not dataset_id:
374
375
  return {"status": "error", "error": "dataset_id required"}
@@ -395,7 +396,7 @@ async def auto_resolve_source_contacts(
395
396
  source = REGISTRY.get(source_id)
396
397
  if not source:
397
398
  return {"status": "error", "error": "unknown source_id"}
398
- if source.source_type != "local_sync":
399
+ if source.delivery != DELIVERY_LOCAL_SYNC:
399
400
  return {"status": "error", "error": "contacts only apply to local_sync sources"}
400
401
  if not dataset_id:
401
402
  return {"status": "error", "error": "dataset_id required"}
@@ -0,0 +1,98 @@
1
+ """Owner data-control API: exclude artifacts from derived intelligence.
2
+
3
+ These endpoints are the "remove things I don't want included" surface:
4
+ GET /intelligence/exclusions — list tombstones
5
+ POST /intelligence/exclusions/fact — soft-close + tombstone a fact
6
+ POST /intelligence/exclusions/entity — stop tracking an entity
7
+ POST /intelligence/exclusions/stat — suppress a stat insight
8
+ POST /intelligence/exclusions/record — purge one record's derived traces
9
+ DELETE /intelligence/exclusions — lift a tombstone (re-inclusion
10
+ happens on the next enrichment)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import Any, Dict, Optional
16
+
17
+ from fastapi import APIRouter, Body, Depends, HTTPException
18
+
19
+ from ..auth import require_api_key
20
+ from ..core.state import get_db_connection
21
+
22
+ router = APIRouter(tags=["intelligence-lifecycle"])
23
+
24
+
25
+ def _store():
26
+ conn = get_db_connection()
27
+ if conn is None:
28
+ raise HTTPException(status_code=503, detail="Database not available")
29
+ from ..features.lifecycle.exclusions import ExclusionStore
30
+
31
+ return ExclusionStore(conn)
32
+
33
+
34
+ @router.get("/intelligence/exclusions", dependencies=[Depends(require_api_key)])
35
+ async def list_exclusions(artifact_type: Optional[str] = None) -> Dict[str, Any]:
36
+ items = _store().list(artifact_type=artifact_type)
37
+ return {"items": items, "total": len(items)}
38
+
39
+
40
+ @router.post("/intelligence/exclusions/fact", dependencies=[Depends(require_api_key)])
41
+ async def exclude_fact(
42
+ subject_entity_id: str = Body(...),
43
+ predicate: str = Body(...),
44
+ object_value: Optional[str] = Body(None),
45
+ note: Optional[str] = Body(None),
46
+ ) -> Dict[str, Any]:
47
+ try:
48
+ return _store().exclude_fact(
49
+ subject_entity_id=subject_entity_id,
50
+ predicate=predicate,
51
+ object_value=object_value,
52
+ note=note,
53
+ )
54
+ except ValueError as exc:
55
+ raise HTTPException(status_code=400, detail=str(exc))
56
+
57
+
58
+ @router.post("/intelligence/exclusions/entity", dependencies=[Depends(require_api_key)])
59
+ async def exclude_entity(
60
+ entity_ref: str = Body(..., description="entity_id or name"),
61
+ note: Optional[str] = Body(None),
62
+ ) -> Dict[str, Any]:
63
+ try:
64
+ return _store().exclude_entity(entity_ref=entity_ref, note=note)
65
+ except ValueError as exc:
66
+ raise HTTPException(status_code=400, detail=str(exc))
67
+
68
+
69
+ @router.post("/intelligence/exclusions/stat", dependencies=[Depends(require_api_key)])
70
+ async def exclude_stat_insight(
71
+ stat_id: str = Body(...),
72
+ group_key: str = Body(""),
73
+ note: Optional[str] = Body(None),
74
+ ) -> Dict[str, Any]:
75
+ try:
76
+ return _store().exclude_stat_insight(stat_id=stat_id, group_key=group_key, note=note)
77
+ except ValueError as exc:
78
+ raise HTTPException(status_code=400, detail=str(exc))
79
+
80
+
81
+ @router.post("/intelligence/exclusions/record", dependencies=[Depends(require_api_key)])
82
+ async def exclude_record(
83
+ record_id: str = Body(...),
84
+ note: Optional[str] = Body(None),
85
+ ) -> Dict[str, Any]:
86
+ try:
87
+ return _store().exclude_record(record_id=record_id, note=note)
88
+ except ValueError as exc:
89
+ raise HTTPException(status_code=400, detail=str(exc))
90
+
91
+
92
+ @router.delete("/intelligence/exclusions", dependencies=[Depends(require_api_key)])
93
+ async def remove_exclusion(
94
+ artifact_type: str = Body(...),
95
+ artifact_key: str = Body(...),
96
+ ) -> Dict[str, Any]:
97
+ removed = _store().remove_exclusion(artifact_type, artifact_key)
98
+ return {"removed": removed}
@@ -197,6 +197,188 @@ async def evaluate_fit(body: FitEvaluateBody, _api_key: str = Depends(require_ap
197
197
  raise HTTPException(status_code=503, detail=str(exc)) from exc
198
198
 
199
199
 
200
+ def _entities_conn():
201
+ from ..core.state import get_db_connection
202
+
203
+ conn = get_db_connection()
204
+ if conn is None:
205
+ raise HTTPException(status_code=503, detail="database_unavailable")
206
+ return conn
207
+
208
+
209
+ @router.get("/entities")
210
+ async def list_entities(
211
+ q: Optional[str] = Query(default=None, max_length=200),
212
+ entity_type: Optional[str] = Query(default=None, max_length=40),
213
+ contacts_only: bool = Query(default=False),
214
+ limit: int = Query(default=50, ge=1, le=200),
215
+ offset: int = Query(default=0, ge=0),
216
+ _api_key: str = Depends(require_api_key),
217
+ ):
218
+ """Resolved entity registry (entity spine), sorted by mention count."""
219
+ from ..features.entities.reads import list_entities as _list
220
+
221
+ return _list(
222
+ _entities_conn(),
223
+ q=q,
224
+ entity_type=entity_type,
225
+ contacts_only=contacts_only,
226
+ limit=limit,
227
+ offset=offset,
228
+ )
229
+
230
+
231
+ # NB: must be declared before /entities/{entity_id} so "graph" isn't captured
232
+ # as an entity id.
233
+ @router.get("/entities/graph")
234
+ async def get_entity_graph(
235
+ limit_nodes: int = Query(default=100, ge=1, le=500),
236
+ limit_edges: int = Query(default=300, ge=1, le=1500),
237
+ min_weight: float = Query(default=0.0, ge=0.0),
238
+ _api_key: str = Depends(require_api_key),
239
+ ):
240
+ """Entity-spine graph (decayed typed edges) in list_graph node/edge shape."""
241
+ from ..features.entities.reads import entity_graph
242
+
243
+ return entity_graph(
244
+ _entities_conn(),
245
+ limit_nodes=limit_nodes,
246
+ limit_edges=limit_edges,
247
+ min_weight=min_weight,
248
+ )
249
+
250
+
251
+ @router.get("/entities/{entity_id}")
252
+ async def get_entity(
253
+ entity_id: str,
254
+ _api_key: str = Depends(require_api_key),
255
+ ):
256
+ """Entity detail: aliases, connections, recent mentions, dossier (owner view)."""
257
+ from ..features.entities.reads import get_entity_detail
258
+
259
+ detail = get_entity_detail(_entities_conn(), entity_id)
260
+ if detail is None:
261
+ raise HTTPException(status_code=404, detail=f"entity not found: {entity_id}")
262
+ return detail
263
+
264
+
265
+ @router.get("/entity-review")
266
+ async def list_entity_review(
267
+ status: str = Query(default="pending", max_length=20),
268
+ limit: int = Query(default=100, ge=1, le=500),
269
+ _api_key: str = Depends(require_api_key),
270
+ ):
271
+ """Pending entity merge candidates (owner curation queue)."""
272
+ from ..features.entities.consolidation import list_review
273
+
274
+ items = list_review(_entities_conn(), status=status, limit=limit)
275
+ return {"items": items, "total": len(items)}
276
+
277
+
278
+ @router.post("/entity-review/sweep")
279
+ async def run_entity_review_sweep(_api_key: str = Depends(require_api_key)):
280
+ """Run the consolidation sweep now; returns proposal counts."""
281
+ from ..features.entities.consolidation import propose_merges
282
+
283
+ return propose_merges(_entities_conn())
284
+
285
+
286
+ @router.post("/entity-review/{review_id}/approve")
287
+ async def approve_entity_review(review_id: str, _api_key: str = Depends(require_api_key)):
288
+ from ..features.entities.consolidation import resolve_review
289
+
290
+ try:
291
+ return resolve_review(_entities_conn(), review_id, action="approve")
292
+ except LookupError as exc:
293
+ raise HTTPException(status_code=404, detail=str(exc))
294
+ except ValueError as exc:
295
+ raise HTTPException(status_code=400, detail=str(exc))
296
+
297
+
298
+ @router.post("/entity-review/{review_id}/dismiss")
299
+ async def dismiss_entity_review(review_id: str, _api_key: str = Depends(require_api_key)):
300
+ from ..features.entities.consolidation import resolve_review
301
+
302
+ try:
303
+ return resolve_review(_entities_conn(), review_id, action="dismiss")
304
+ except LookupError as exc:
305
+ raise HTTPException(status_code=404, detail=str(exc))
306
+ except ValueError as exc:
307
+ raise HTTPException(status_code=400, detail=str(exc))
308
+
309
+
310
+ @router.post("/entities/{entity_id}/exclude")
311
+ async def exclude_entity_from_intelligence(
312
+ entity_id: str,
313
+ _api_key: str = Depends(require_api_key),
314
+ ):
315
+ """Owner exclusion: stop tracking this entity (tombstoned, reversible)."""
316
+ from ..features.lifecycle.exclusions import ExclusionStore
317
+
318
+ try:
319
+ return ExclusionStore(_entities_conn()).exclude_entity(entity_ref=entity_id)
320
+ except ValueError as exc:
321
+ raise HTTPException(status_code=400, detail=str(exc))
322
+
323
+
324
+ @router.get("/facts")
325
+ async def list_facts(
326
+ predicate: Optional[str] = Query(default=None, max_length=80),
327
+ dimension: Optional[str] = Query(default=None, max_length=40),
328
+ include_closed: bool = Query(default=False),
329
+ limit: int = Query(default=100, ge=1, le=500),
330
+ offset: int = Query(default=0, ge=0),
331
+ _api_key: str = Depends(require_api_key),
332
+ ):
333
+ """Atomic owner facts with temporal validity (belief history via include_closed)."""
334
+ from ..features.facts.reads import list_facts as _list
335
+
336
+ return _list(
337
+ _entities_conn(),
338
+ predicate=predicate,
339
+ dimension=dimension,
340
+ include_closed=include_closed,
341
+ limit=limit,
342
+ offset=offset,
343
+ )
344
+
345
+
346
+ @router.get("/insights")
347
+ async def list_stat_insights(
348
+ dimension: Optional[str] = Query(default=None, max_length=40),
349
+ limit: int = Query(default=200, ge=1, le=500),
350
+ _api_key: str = Depends(require_api_key),
351
+ ):
352
+ """Promoted statistical insights (owner-only rhythms, session stats, trends)."""
353
+ from ..features.facts.reads import list_stat_insights as _list
354
+
355
+ return _list(_entities_conn(), dimension=dimension, limit=limit)
356
+
357
+
358
+ @router.get("/timeline")
359
+ async def list_timeline(
360
+ canonical_table: Optional[str] = Query(default=None, max_length=60),
361
+ source_id: Optional[str] = Query(default=None, max_length=120),
362
+ date_from: Optional[str] = Query(default=None, max_length=32),
363
+ date_to: Optional[str] = Query(default=None, max_length=32),
364
+ limit: int = Query(default=100, ge=1, le=500),
365
+ offset: int = Query(default=0, ge=0),
366
+ _api_key: str = Depends(require_api_key),
367
+ ):
368
+ """Unified temporal projection across canonical tables (newest first)."""
369
+ from ..features.facts.reads import list_timeline as _list
370
+
371
+ return _list(
372
+ _entities_conn(),
373
+ canonical_table=canonical_table,
374
+ source_id=source_id,
375
+ date_from=date_from,
376
+ date_to=date_to,
377
+ limit=limit,
378
+ offset=offset,
379
+ )
380
+
381
+
200
382
  @router.get("/definitions")
201
383
  async def list_signal_definitions(_api_key: str = Depends(require_api_key)):
202
384
  service = get_signal_service()
@@ -183,12 +183,21 @@ async def _test_ingestion_core(payload: Dict[str, Any]) -> Dict[str, Any]:
183
183
  if not source_def:
184
184
  raise LookupError(f"No active install found for source_id={source_id}")
185
185
 
186
+ from ..sources.definitions import (
187
+ DELIVERY_CLIENT_PUSH,
188
+ DELIVERY_OWNER_UI,
189
+ DELIVERY_OWNER_UPLOAD,
190
+ derive_delivery,
191
+ )
192
+
186
193
  source_type = str(source_def.get("source_type") or "file")
194
+ # Installed definitions serialized before the delivery rollout lack the field.
195
+ delivery = str(source_def.get("delivery") or "").strip() or derive_delivery(source_type, source_id)
187
196
  schema_id = str(source_def.get("schema_id") or source_def.get("parser_id") or "").strip()
188
197
  if not schema_id:
189
198
  raise ValueError("Installed source definition is missing schema_id/parser_id")
190
199
 
191
- if source_type == "file":
200
+ if delivery == DELIVERY_OWNER_UPLOAD:
192
201
  file_path = str(payload.get("sample_file_path") or "").strip()
193
202
  if not file_path:
194
203
  raise ValueError("sample_file_path is required for file source tests")
@@ -198,7 +207,7 @@ async def _test_ingestion_core(payload: Dict[str, Any]) -> Dict[str, Any]:
198
207
  file_path=file_path,
199
208
  source_id=source_id,
200
209
  )
201
- elif source_type == "ui_stream":
210
+ elif delivery in (DELIVERY_CLIENT_PUSH, DELIVERY_OWNER_UI):
202
211
  sample_payload = payload.get("sample_payload")
203
212
  if not isinstance(sample_payload, dict):
204
213
  raise ValueError("sample_payload object is required for ui_stream source tests")
@@ -24,6 +24,7 @@ from .api import (
24
24
  ingestion_compat as ingestion_compat_routes,
25
25
  ingestion_api as ingestion_routes,
26
26
  ingestion_sources as ingestion_sources_routes,
27
+ intelligence_lifecycle as intelligence_lifecycle_routes,
27
28
  local_mcp as local_mcp_routes,
28
29
  llm as llm_routes,
29
30
  messenger_analytics as messenger_analytics_routes,
@@ -101,6 +102,7 @@ app.include_router(analytics_routes.router)
101
102
  app.include_router(sources_routes.router)
102
103
  app.include_router(source_install_routes.router, prefix="/v1")
103
104
  app.include_router(source_scrub_routes.router, prefix="/v1")
105
+ app.include_router(intelligence_lifecycle_routes.router, prefix="/v1")
104
106
  app.include_router(app_registry_routes.router)
105
107
  app.include_router(ingestion_compat_routes.router)
106
108
  app.include_router(enrichment_routes.router, prefix="/v1")
@@ -100,6 +100,10 @@ class Settings(BaseSettings):
100
100
  engine_ollama_base_url: str = Field("http://localhost:11434", env="ENGINE_OLLAMA_BASE_URL")
101
101
  ollama_query_model: str = Field("llama3.2:latest", env="TOPOS_OLLAMA_QUERY_MODEL")
102
102
  engine_default_provider: str = Field("huggingface", env="ENGINE_DEFAULT_PROVIDER")
103
+ # §D minimizer runs on EVERY grantee query, so it uses a small/fast local model. The judge
104
+ # only runs in nightly privacy evals (F.4/CER semantic scoring), so it can be larger/slower.
105
+ disclosure_minimizer_model: str = Field("llama3.2:latest", env="TOPOS_DISCLOSURE_MINIMIZER_MODEL")
106
+ privacy_judge_model: str = Field("qwen3.5:9b-mlx", env="TOPOS_PRIVACY_JUDGE_MODEL")
103
107
 
104
108
  engine_max_resident_models: int = Field(3, env="ENGINE_MAX_RESIDENT_MODELS")
105
109
  engine_model_idle_ttl_sec: int = Field(0, env="ENGINE_MODEL_IDLE_TTL_SEC")
@@ -319,6 +319,14 @@ class ControlPlaneClient:
319
319
  return False
320
320
 
321
321
  async def _handle_message(self, ws, data: Dict[str, Any]) -> None:
322
+ msg_type = str(data.get("type") or "").strip().lower()
323
+ if msg_type == "ping":
324
+ pong: Dict[str, Any] = {"type": "pong"}
325
+ ping_id = data.get("id")
326
+ if ping_id is not None:
327
+ pong["id"] = ping_id
328
+ await self._send_ws_json(ws, pong)
329
+ return
322
330
  try:
323
331
  resp = await self.handler(data)
324
332
  except Exception as exc: # noqa: BLE001