topos-node 1.2.0__tar.gz → 1.2.1__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 (522) hide show
  1. {topos_node-1.2.0/topos_node.egg-info → topos_node-1.2.1}/PKG-INFO +1 -1
  2. {topos_node-1.2.0 → topos_node-1.2.1}/pyproject.toml +1 -1
  3. {topos_node-1.2.0 → topos_node-1.2.1}/topos/__version__.py +2 -2
  4. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/enrichment.py +10 -1
  5. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/signal.py +23 -0
  6. topos_node-1.2.1/topos/api/upgrades.py +30 -0
  7. {topos_node-1.2.0 → topos_node-1.2.1}/topos/app.py +15 -0
  8. {topos_node-1.2.0 → topos_node-1.2.1}/topos/control_plane_client.py +6 -2
  9. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/config.py +14 -0
  10. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/signal_features.py +23 -0
  11. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/entities/consolidation.py +98 -0
  12. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/entities/fact_materializer.py +8 -3
  13. topos_node-1.2.1/topos/features/entities/graph_enrichers.py +393 -0
  14. topos_node-1.2.1/topos/features/entities/graph_refresh.py +145 -0
  15. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/entities/resolver.py +23 -3
  16. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/stats/definitions.py +5 -1
  17. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/canonical_pipeline.py +10 -0
  18. {topos_node-1.2.0 → topos_node-1.2.1}/topos/protocol/handled_message_types.json +2 -0
  19. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/retrieval.py +47 -13
  20. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/scope_registry.json +3 -1
  21. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/__init__.py +6 -0
  22. topos_node-1.2.1/topos/storage/db/migrations/derivation_ledger_v1.py +44 -0
  23. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sync/client.py +6 -2
  24. topos_node-1.2.1/topos/upgrades/runner.py +339 -0
  25. {topos_node-1.2.0 → topos_node-1.2.1/topos_node.egg-info}/PKG-INFO +1 -1
  26. {topos_node-1.2.0 → topos_node-1.2.1}/topos_node.egg-info/SOURCES.txt +4 -0
  27. topos_node-1.2.0/topos/features/entities/graph_enrichers.py +0 -211
  28. {topos_node-1.2.0 → topos_node-1.2.1}/LICENSE +0 -0
  29. {topos_node-1.2.0 → topos_node-1.2.1}/MANIFEST.in +0 -0
  30. {topos_node-1.2.0 → topos_node-1.2.1}/README.md +0 -0
  31. {topos_node-1.2.0 → topos_node-1.2.1}/setup.cfg +0 -0
  32. {topos_node-1.2.0 → topos_node-1.2.1}/shared/__init__.py +0 -0
  33. {topos_node-1.2.0 → topos_node-1.2.1}/shared/filtering.py +0 -0
  34. {topos_node-1.2.0 → topos_node-1.2.1}/shared/schema_registry.py +0 -0
  35. {topos_node-1.2.0 → topos_node-1.2.1}/tests/test_llm_integrations_storage.py +0 -0
  36. {topos_node-1.2.0 → topos_node-1.2.1}/tests/test_protocol_handled_types.py +0 -0
  37. {topos_node-1.2.0 → topos_node-1.2.1}/topos/__init__.py +0 -0
  38. {topos_node-1.2.0 → topos_node-1.2.1}/topos/analytics/__init__.py +0 -0
  39. {topos_node-1.2.0 → topos_node-1.2.1}/topos/analytics/duckdb_adapter.py +0 -0
  40. {topos_node-1.2.0 → topos_node-1.2.1}/topos/analytics/messenger_communities.py +0 -0
  41. {topos_node-1.2.0 → topos_node-1.2.1}/topos/analytics/messenger_graph.py +0 -0
  42. {topos_node-1.2.0 → topos_node-1.2.1}/topos/analytics/messenger_labels.py +0 -0
  43. {topos_node-1.2.0 → topos_node-1.2.1}/topos/analytics/profiles.py +0 -0
  44. {topos_node-1.2.0 → topos_node-1.2.1}/topos/analytics/query_engine.py +0 -0
  45. {topos_node-1.2.0 → topos_node-1.2.1}/topos/analytics/raw_queries.py +0 -0
  46. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/__init__.py +0 -0
  47. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/analytics.py +0 -0
  48. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/app_registry.py +0 -0
  49. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/backup.py +0 -0
  50. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/compute_remote.py +0 -0
  51. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/data_commit.py +0 -0
  52. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/data_explorer_table_prefs.py +0 -0
  53. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/database_explorer.py +0 -0
  54. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/db.py +0 -0
  55. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/device.py +0 -0
  56. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/engine_run.py +0 -0
  57. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/enrichment_lab.py +0 -0
  58. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/filter_lab.py +0 -0
  59. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/health.py +0 -0
  60. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/home_chat.py +0 -0
  61. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/ingestion_api.py +0 -0
  62. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/ingestion_compat.py +0 -0
  63. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/ingestion_sources.py +0 -0
  64. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/intelligence_lifecycle.py +0 -0
  65. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/llm.py +0 -0
  66. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/local_mcp.py +0 -0
  67. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/messenger_analytics.py +0 -0
  68. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/privacy_disclose.py +0 -0
  69. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/query_api.py +0 -0
  70. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/runtime_bootstrap.py +0 -0
  71. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/sanitization_ollama_config.py +0 -0
  72. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/signal_extraction_config.py +0 -0
  73. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/source_install.py +0 -0
  74. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/source_scrub.py +0 -0
  75. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/sources.py +0 -0
  76. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/sources_overview.py +0 -0
  77. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/sync.py +0 -0
  78. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/ui_config.py +0 -0
  79. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/uma_data.py +0 -0
  80. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/usage.py +0 -0
  81. {topos_node-1.2.0 → topos_node-1.2.1}/topos/api/user_identity.py +0 -0
  82. {topos_node-1.2.0 → topos_node-1.2.1}/topos/auth.py +0 -0
  83. {topos_node-1.2.0 → topos_node-1.2.1}/topos/canonicalization/__init__.py +0 -0
  84. {topos_node-1.2.0 → topos_node-1.2.1}/topos/canonicalization/mappers/__init__.py +0 -0
  85. {topos_node-1.2.0 → topos_node-1.2.1}/topos/canonicalization/mappers/base.py +0 -0
  86. {topos_node-1.2.0 → topos_node-1.2.1}/topos/canonicalization/mappers/browser_activity_mapper.py +0 -0
  87. {topos_node-1.2.0 → topos_node-1.2.1}/topos/canonicalization/mappers/chatgpt_mapper.py +0 -0
  88. {topos_node-1.2.0 → topos_node-1.2.1}/topos/canonicalization/mappers/demo_mappers.py +0 -0
  89. {topos_node-1.2.0 → topos_node-1.2.1}/topos/canonicalization/mappers/github_activity_mapper.py +0 -0
  90. {topos_node-1.2.0 → topos_node-1.2.1}/topos/canonicalization/mappers/grok_mapper.py +0 -0
  91. {topos_node-1.2.0 → topos_node-1.2.1}/topos/canonicalization/mappers/messenger_mapper.py +0 -0
  92. {topos_node-1.2.0 → topos_node-1.2.1}/topos/canonicalization/models.py +0 -0
  93. {topos_node-1.2.0 → topos_node-1.2.1}/topos/canonicalization/resolver.py +0 -0
  94. {topos_node-1.2.0 → topos_node-1.2.1}/topos/cli/__init__.py +0 -0
  95. {topos_node-1.2.0 → topos_node-1.2.1}/topos/cli/__main__.py +0 -0
  96. {topos_node-1.2.0 → topos_node-1.2.1}/topos/cli/commands.py +0 -0
  97. {topos_node-1.2.0 → topos_node-1.2.1}/topos/config/__init__.py +0 -0
  98. {topos_node-1.2.0 → topos_node-1.2.1}/topos/config/sanitization_ollama.py +0 -0
  99. {topos_node-1.2.0 → topos_node-1.2.1}/topos/config/settings.py +0 -0
  100. {topos_node-1.2.0 → topos_node-1.2.1}/topos/config/signal_extraction.py +0 -0
  101. {topos_node-1.2.0 → topos_node-1.2.1}/topos/config/uvicorn_logging.json +0 -0
  102. {topos_node-1.2.0 → topos_node-1.2.1}/topos/contacts/__init__.py +0 -0
  103. {topos_node-1.2.0 → topos_node-1.2.1}/topos/contacts/identity.py +0 -0
  104. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/__init__.py +0 -0
  105. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/api_models.py +0 -0
  106. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/connection_resilience.py +0 -0
  107. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/device_helpers.py +0 -0
  108. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/errors.py +0 -0
  109. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/events.py +0 -0
  110. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/__init__.py +0 -0
  111. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/common.py +0 -0
  112. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/contacts_import.py +0 -0
  113. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/database_explorer.py +0 -0
  114. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/device.py +0 -0
  115. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/enrichment.py +0 -0
  116. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/enrichment_lab.py +0 -0
  117. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/filter_lab.py +0 -0
  118. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/home_chat.py +0 -0
  119. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/ingest.py +0 -0
  120. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/messages.py +0 -0
  121. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/messenger_analytics.py +0 -0
  122. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/query.py +0 -0
  123. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/registry.py +0 -0
  124. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/routines.py +0 -0
  125. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/sources.py +0 -0
  126. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/handlers/uma.py +0 -0
  127. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/logging.py +0 -0
  128. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/metrics.py +0 -0
  129. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/routine_access.py +0 -0
  130. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/startup_banner.py +0 -0
  131. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/state.py +0 -0
  132. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/table_layers.py +0 -0
  133. {topos_node-1.2.0 → topos_node-1.2.1}/topos/core/types.py +0 -0
  134. {topos_node-1.2.0 → topos_node-1.2.1}/topos/data_explorer_row_delete.py +0 -0
  135. {topos_node-1.2.0 → topos_node-1.2.1}/topos/data_explorer_table_prefs.py +0 -0
  136. {topos_node-1.2.0 → topos_node-1.2.1}/topos/data_explorer_tables.py +0 -0
  137. {topos_node-1.2.0 → topos_node-1.2.1}/topos/disclosure/__init__.py +0 -0
  138. {topos_node-1.2.0 → topos_node-1.2.1}/topos/disclosure/canonical_writer.py +0 -0
  139. {topos_node-1.2.0 → topos_node-1.2.1}/topos/disclosure/content_policy.py +0 -0
  140. {topos_node-1.2.0 → topos_node-1.2.1}/topos/disclosure/field_registry.py +0 -0
  141. {topos_node-1.2.0 → topos_node-1.2.1}/topos/disclosure/privacy_layer.py +0 -0
  142. {topos_node-1.2.0 → topos_node-1.2.1}/topos/disclosure/tier.py +0 -0
  143. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/__init__.py +0 -0
  144. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/backends/__init__.py +0 -0
  145. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/backends/base.py +0 -0
  146. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/backends/generative_prompts.py +0 -0
  147. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/backends/generative_response.py +0 -0
  148. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/backends/huggingface.py +0 -0
  149. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/backends/ollama.py +0 -0
  150. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/backends/openai.py +0 -0
  151. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/backends/openai_compatible.py +0 -0
  152. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/backends/redpill.py +0 -0
  153. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/backends/stub.py +0 -0
  154. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/client.py +0 -0
  155. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/engine.py +0 -0
  156. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/intake.py +0 -0
  157. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/memory_utils.py +0 -0
  158. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/model_cache.py +0 -0
  159. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/pipeline_memory.py +0 -0
  160. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/queue_manager.py +0 -0
  161. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/registration.py +0 -0
  162. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/result_formatter.py +0 -0
  163. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/router.py +0 -0
  164. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/scoped_token.py +0 -0
  165. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/tasks.py +0 -0
  166. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/transport.py +0 -0
  167. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/usage_guard.py +0 -0
  168. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/usage_observation.py +0 -0
  169. {topos_node-1.2.0 → topos_node-1.2.1}/topos/engine/validator.py +0 -0
  170. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/__init__.py +0 -0
  171. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/catalog.py +0 -0
  172. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/derived_tables.py +0 -0
  173. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/job_writer.py +0 -0
  174. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/__init__.py +0 -0
  175. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/base.py +0 -0
  176. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/__init__.py +0 -0
  177. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/_batch_limits.py +0 -0
  178. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/_engine_runner.py +0 -0
  179. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/availability_scores_job.py +0 -0
  180. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/brief_fallback.py +0 -0
  181. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/dimension_summary_job.py +0 -0
  182. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/embeddings_job.py +0 -0
  183. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/emo_27_job.py +0 -0
  184. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/entities_job.py +0 -0
  185. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/fact_extraction_job.py +0 -0
  186. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/goal_extraction_job.py +0 -0
  187. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/pii_redaction_job.py +0 -0
  188. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/relationship_edges_job.py +0 -0
  189. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/sentiment_job.py +0 -0
  190. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/statistics_job.py +0 -0
  191. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/timeline_job.py +0 -0
  192. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/topic_clusters_job.py +0 -0
  193. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/topics_job.py +0 -0
  194. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/url_classification_core.py +0 -0
  195. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/url_classification_enrichment_job.py +0 -0
  196. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/canonical/url_classification_signal_job.py +0 -0
  197. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/raw/__init__.py +0 -0
  198. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/raw/attachments_job.py +0 -0
  199. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/raw/language_job.py +0 -0
  200. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/raw/time_normalization_job.py +0 -0
  201. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/jobs/raw/tool_calls_job.py +0 -0
  202. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/model_overrides.py +0 -0
  203. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/models/__init__.py +0 -0
  204. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/models/manager.py +0 -0
  205. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/models/mvp_defaults.py +0 -0
  206. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/models/registry.py +0 -0
  207. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/models/versioning.py +0 -0
  208. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/orchestrator.py +0 -0
  209. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/processor.py +0 -0
  210. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/progress_bar.py +0 -0
  211. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/registry_contract.py +0 -0
  212. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/signal_derivation_config.json +0 -0
  213. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/source_overrides.py +0 -0
  214. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment/website_classifier.py +0 -0
  215. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment_lab/__init__.py +0 -0
  216. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment_lab/bundles.py +0 -0
  217. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment_lab/model_resolve.py +0 -0
  218. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment_lab/schema.py +0 -0
  219. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment_lab/service.py +0 -0
  220. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment_lab/store.py +0 -0
  221. {topos_node-1.2.0 → topos_node-1.2.1}/topos/enrichment_lab/worker.py +0 -0
  222. {topos_node-1.2.0 → topos_node-1.2.1}/topos/evals/__init__.py +0 -0
  223. {topos_node-1.2.0 → topos_node-1.2.1}/topos/evals/enrichment_eval.py +0 -0
  224. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/entities/__init__.py +0 -0
  225. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/entities/dossier.py +0 -0
  226. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/entities/edges.py +0 -0
  227. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/entities/graph_search.py +0 -0
  228. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/entities/linking.py +0 -0
  229. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/entities/maintenance.py +0 -0
  230. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/entities/reads.py +0 -0
  231. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/facts/__init__.py +0 -0
  232. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/facts/extract.py +0 -0
  233. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/facts/llm_extract.py +0 -0
  234. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/facts/reads.py +0 -0
  235. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/facts/store.py +0 -0
  236. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/fit/evaluator.py +0 -0
  237. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/fit/opportunity_types.json +0 -0
  238. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/lifecycle/__init__.py +0 -0
  239. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/lifecycle/derived_scrub.py +0 -0
  240. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/lifecycle/exclusions.py +0 -0
  241. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/lifecycle/gc.py +0 -0
  242. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/provenance/__init__.py +0 -0
  243. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/provenance/posture.py +0 -0
  244. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/provenance/roles.py +0 -0
  245. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/__init__.py +0 -0
  246. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/brief_canonical_loader.py +0 -0
  247. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/brief_ontology.py +0 -0
  248. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/brief_renderer.py +0 -0
  249. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/brief_schemas.py +0 -0
  250. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/chunking.py +0 -0
  251. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/cluster_labels.py +0 -0
  252. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/data_health.py +0 -0
  253. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/definitions/brief_ontologies.json +0 -0
  254. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/definitions/intentions.json +0 -0
  255. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/definitions/profile.json +0 -0
  256. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/definitions/relationships.json +0 -0
  257. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/definitions/schema/dimension_definition.schema.json +0 -0
  258. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/definitions/time.json +0 -0
  259. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/definitions/work.json +0 -0
  260. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/dimension_briefs.py +0 -0
  261. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/dimension_definition_loader.py +0 -0
  262. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/dimension_profiles.py +0 -0
  263. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/dimension_registry.py +0 -0
  264. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/embed_context.py +0 -0
  265. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/extraction/artifact_router.py +0 -0
  266. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/extraction/artifact_store.py +0 -0
  267. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/extraction/rule_extractors.py +0 -0
  268. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/graph_sanitize.py +0 -0
  269. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/hybrid_search.py +0 -0
  270. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/incremental_clustering.py +0 -0
  271. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/model_recommendations.py +0 -0
  272. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/query_embed_cache.py +0 -0
  273. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/schemas.py +0 -0
  274. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/service.py +0 -0
  275. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/signal_object_store.py +0 -0
  276. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/source_hydration.py +0 -0
  277. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/structured_signal.py +0 -0
  278. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/topic_clustering.py +0 -0
  279. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/typed_stores/aggregates.py +0 -0
  280. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/typed_stores/scope_materializer.py +0 -0
  281. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/vector_codec.py +0 -0
  282. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/vector_math.py +0 -0
  283. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/signal/vector_settings.py +0 -0
  284. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/stats/__init__.py +0 -0
  285. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/stats/engine.py +0 -0
  286. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/stats/fold.py +0 -0
  287. {topos_node-1.2.0 → topos_node-1.2.1}/topos/features/stats/insights.py +0 -0
  288. {topos_node-1.2.0 → topos_node-1.2.1}/topos/filter_lab/__init__.py +0 -0
  289. {topos_node-1.2.0 → topos_node-1.2.1}/topos/filter_lab/bundles.py +0 -0
  290. {topos_node-1.2.0 → topos_node-1.2.1}/topos/filter_lab/schema.py +0 -0
  291. {topos_node-1.2.0 → topos_node-1.2.1}/topos/filter_lab/service.py +0 -0
  292. {topos_node-1.2.0 → topos_node-1.2.1}/topos/filter_lab/store.py +0 -0
  293. {topos_node-1.2.0 → topos_node-1.2.1}/topos/filter_lab/worker.py +0 -0
  294. {topos_node-1.2.0 → topos_node-1.2.1}/topos/home_chat/__init__.py +0 -0
  295. {topos_node-1.2.0 → topos_node-1.2.1}/topos/home_chat/schema.py +0 -0
  296. {topos_node-1.2.0 → topos_node-1.2.1}/topos/home_chat/store.py +0 -0
  297. {topos_node-1.2.0 → topos_node-1.2.1}/topos/hosted_pool_lease.py +0 -0
  298. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/__init__.py +0 -0
  299. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/checkpoints/__init__.py +0 -0
  300. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/checkpoints/checkpoint_store.py +0 -0
  301. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/checkpoints/sqlite_checkpoint_store.py +0 -0
  302. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/inbox_crypto.py +0 -0
  303. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/inbox_drain.py +0 -0
  304. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/ingest_helpers.py +0 -0
  305. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/jobs.py +0 -0
  306. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/journal_location_fanout.py +0 -0
  307. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/journal_time_log_normalize.py +0 -0
  308. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/local_sync.py +0 -0
  309. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/log_preview.py +0 -0
  310. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/manager.py +0 -0
  311. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parser.py +0 -0
  312. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parsers/__init__.py +0 -0
  313. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parsers/base.py +0 -0
  314. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parsers/browser_parser.py +0 -0
  315. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parsers/calendar_parser.py +0 -0
  316. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parsers/chatgpt_conversation_flattener.py +0 -0
  317. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parsers/chatgpt_parser.py +0 -0
  318. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parsers/demo_file_parsers.py +0 -0
  319. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parsers/github_activity_parser.py +0 -0
  320. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parsers/grok_parser.py +0 -0
  321. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parsers/messenger_parser.py +0 -0
  322. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/parsers/voxterm_parser.py +0 -0
  323. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/progress.py +0 -0
  324. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/reprocess.py +0 -0
  325. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/sources/__init__.py +0 -0
  326. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/sources/base.py +0 -0
  327. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/sources/calendar.py +0 -0
  328. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/sources/chatgpt.py +0 -0
  329. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/sources/contact_importers.py +0 -0
  330. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/sources/grok.py +0 -0
  331. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/sources/imessage_reader.py +0 -0
  332. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/sources/signal_export_parser.py +0 -0
  333. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/sources/signal_reader.py +0 -0
  334. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/state_machine.py +0 -0
  335. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/triggers/__init__.py +0 -0
  336. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/triggers/file_trigger.py +0 -0
  337. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/triggers/sqlite_trigger.py +0 -0
  338. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/usage_inbox_dedupe.py +0 -0
  339. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/validation/__init__.py +0 -0
  340. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/validation/base.py +0 -0
  341. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/validation/schema_registry.py +0 -0
  342. {topos_node-1.2.0 → topos_node-1.2.1}/topos/ingestion/validation/schema_validator.py +0 -0
  343. {topos_node-1.2.0 → topos_node-1.2.1}/topos/lineage/__init__.py +0 -0
  344. {topos_node-1.2.0 → topos_node-1.2.1}/topos/lineage/provenance.py +0 -0
  345. {topos_node-1.2.0 → topos_node-1.2.1}/topos/lineage/tracker.py +0 -0
  346. {topos_node-1.2.0 → topos_node-1.2.1}/topos/llm_integrations_storage.py +0 -0
  347. {topos_node-1.2.0 → topos_node-1.2.1}/topos/mcp_stdio_proxy.py +0 -0
  348. {topos_node-1.2.0 → topos_node-1.2.1}/topos/observability/__init__.py +0 -0
  349. {topos_node-1.2.0 → topos_node-1.2.1}/topos/observability/alerts.py +0 -0
  350. {topos_node-1.2.0 → topos_node-1.2.1}/topos/observability/metrics.py +0 -0
  351. {topos_node-1.2.0 → topos_node-1.2.1}/topos/observability/tracing.py +0 -0
  352. {topos_node-1.2.0 → topos_node-1.2.1}/topos/openai_client.py +0 -0
  353. {topos_node-1.2.0 → topos_node-1.2.1}/topos/pipeline/__init__.py +0 -0
  354. {topos_node-1.2.0 → topos_node-1.2.1}/topos/pipeline/audit.py +0 -0
  355. {topos_node-1.2.0 → topos_node-1.2.1}/topos/pipeline/envelope.py +0 -0
  356. {topos_node-1.2.0 → topos_node-1.2.1}/topos/pipeline/stages.py +0 -0
  357. {topos_node-1.2.0 → topos_node-1.2.1}/topos/pipeline/stub_enqueue.py +0 -0
  358. {topos_node-1.2.0 → topos_node-1.2.1}/topos/projections/__init__.py +0 -0
  359. {topos_node-1.2.0 → topos_node-1.2.1}/topos/projections/vector_index/__init__.py +0 -0
  360. {topos_node-1.2.0 → topos_node-1.2.1}/topos/projections/vector_index/base.py +0 -0
  361. {topos_node-1.2.0 → topos_node-1.2.1}/topos/projections/vector_index/builders.py +0 -0
  362. {topos_node-1.2.0 → topos_node-1.2.1}/topos/projections/vector_index/health_checks.py +0 -0
  363. {topos_node-1.2.0 → topos_node-1.2.1}/topos/protocol/__init__.py +0 -0
  364. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/__init__.py +0 -0
  365. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/audit.py +0 -0
  366. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/ddr.py +0 -0
  367. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/disclosure.py +0 -0
  368. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/fingerprint.py +0 -0
  369. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/game_layer.py +0 -0
  370. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/grant_cache.py +0 -0
  371. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/inference.py +0 -0
  372. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/intent.py +0 -0
  373. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/manifest.py +0 -0
  374. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/manifest_validation.py +0 -0
  375. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/minimizer.py +0 -0
  376. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/negotiation.py +0 -0
  377. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/pipeline.py +0 -0
  378. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/planner.py +0 -0
  379. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/runtime.py +0 -0
  380. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/scope_registry_loader.py +0 -0
  381. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/session.py +0 -0
  382. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/session_utils.py +0 -0
  383. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/source_generation.py +0 -0
  384. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/turn_classifier.py +0 -0
  385. {topos_node-1.2.0 → topos_node-1.2.1}/topos/query/types.py +0 -0
  386. {topos_node-1.2.0 → topos_node-1.2.1}/topos/rate_limit.py +0 -0
  387. {topos_node-1.2.0 → topos_node-1.2.1}/topos/routines/__init__.py +0 -0
  388. {topos_node-1.2.0 → topos_node-1.2.1}/topos/routines/schema.py +0 -0
  389. {topos_node-1.2.0 → topos_node-1.2.1}/topos/routines/store.py +0 -0
  390. {topos_node-1.2.0 → topos_node-1.2.1}/topos/runtime_update.py +0 -0
  391. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sanitization/__init__.py +0 -0
  392. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sanitization/nsfw_classifier.py +0 -0
  393. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sanitization/ollama_transforms.py +0 -0
  394. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sanitization/privacy_filter.py +0 -0
  395. {topos_node-1.2.0 → topos_node-1.2.1}/topos/scope_resolution.py +0 -0
  396. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/__init__.py +0 -0
  397. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/container.py +0 -0
  398. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/embeddings/__init__.py +0 -0
  399. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/embeddings/base.py +0 -0
  400. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/embeddings/local.py +0 -0
  401. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/embeddings/remote.py +0 -0
  402. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/interfaces.py +0 -0
  403. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/llm/__init__.py +0 -0
  404. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/llm/base.py +0 -0
  405. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/llm/openai.py +0 -0
  406. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/local.py +0 -0
  407. {topos_node-1.2.0 → topos_node-1.2.1}/topos/services/postgres.py +0 -0
  408. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sources/__init__.py +0 -0
  409. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sources/bundled_canonical_triples.py +0 -0
  410. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sources/canonical_signal_defaults.py +0 -0
  411. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sources/definitions.py +0 -0
  412. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sources/install_service.py +0 -0
  413. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sources/registry.py +0 -0
  414. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sources/runtime_install.py +0 -0
  415. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sources/scrub_attribution.py +0 -0
  416. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sources/scrub_service.py +0 -0
  417. {topos_node-1.2.0 → topos_node-1.2.1}/topos/startup_banner.py +0 -0
  418. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/__init__.py +0 -0
  419. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/adapters/__init__.py +0 -0
  420. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/adapters/factory.py +0 -0
  421. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/adapters/fakes.py +0 -0
  422. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/adapters/postgres/stores.py +0 -0
  423. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/adapters/protocols.py +0 -0
  424. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/adapters/sqlite/__init__.py +0 -0
  425. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/adapters/sqlite/stores.py +0 -0
  426. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/adapters/sqlite/vector_search.py +0 -0
  427. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/__init__.py +0 -0
  428. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/activity_tables.py +0 -0
  429. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/ai_chat/__init__.py +0 -0
  430. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/ai_chat/canonicalizer.py +0 -0
  431. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/ai_chat/mapper.py +0 -0
  432. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/ai_chat/model.py +0 -0
  433. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/ai_chat/tables.py +0 -0
  434. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/calendar_contacts_tables.py +0 -0
  435. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/canonical_store.py +0 -0
  436. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/conversations_tables.py +0 -0
  437. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/mapping_store.py +0 -0
  438. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/canonical/postgres.py +0 -0
  439. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/__init__.py +0 -0
  440. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/client.py +0 -0
  441. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/connection_tuning.py +0 -0
  442. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/activity_events_content_v1.py +0 -0
  443. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/actor_role_v1.py +0 -0
  444. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/canonical_disclosure_v1.py +0 -0
  445. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/canonical_nsfw_v1.py +0 -0
  446. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/entity_edges_validity_v1.py +0 -0
  447. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/episodes_v1.py +0 -0
  448. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/extraction_artifacts.py +0 -0
  449. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/journal_entries_duration_v1.py +0 -0
  450. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/journal_entries_ends_at_v1.py +0 -0
  451. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/journal_entries_people_v1.py +0 -0
  452. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/journal_entries_place_name_v1.py +0 -0
  453. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/journal_entries_starts_at_v1.py +0 -0
  454. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/remediation_person_model.py +0 -0
  455. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/scope_source_generation.py +0 -0
  456. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/signal_dimension_backfill_v1.py +0 -0
  457. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/signal_dimension_harness.py +0 -0
  458. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/signal_objects.py +0 -0
  459. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/signal_objects_period_v1.py +0 -0
  460. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/signal_objects_updated_by_v1.py +0 -0
  461. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/stage9_column_renames.py +0 -0
  462. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/vector_storage_v1.py +0 -0
  463. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/vector_storage_v2.py +0 -0
  464. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/vector_storage_v3.py +0 -0
  465. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/vector_storage_v4.py +0 -0
  466. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_clusters_v2.py +0 -0
  467. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_entities_v1.py +0 -0
  468. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_entity_review_v2.py +0 -0
  469. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_facts_v1.py +0 -0
  470. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_lifecycle_v1.py +0 -0
  471. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_mvp_phase0.py +0 -0
  472. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_mvp_phase1.py +0 -0
  473. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_mvp_phase4_messages_cutover.py +0 -0
  474. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_mvp_phase5_topic_clusters.py +0 -0
  475. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_mvp_phase6_dimension_briefs.py +0 -0
  476. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_mvp_query_quality.py +0 -0
  477. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_mvp_topic_clusters_coordination.py +0 -0
  478. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_stats_v1.py +0 -0
  479. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/migrations/wiki_timeline_v1.py +0 -0
  480. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/paths.py +0 -0
  481. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/postgres.py +0 -0
  482. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/schema.py +0 -0
  483. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/db/storage_breakdown.py +0 -0
  484. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/enrichment/__init__.py +0 -0
  485. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/enrichment/canonical_enrichment_store.py +0 -0
  486. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/enrichment/raw_enrichment_store.py +0 -0
  487. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/normalized/__init__.py +0 -0
  488. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/normalized/normalized_store.py +0 -0
  489. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/oplog/__init__.py +0 -0
  490. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/oplog/decision.py +0 -0
  491. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/oplog/oplog_store.py +0 -0
  492. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/oplog/postgres.py +0 -0
  493. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/person/identity_resolver.py +0 -0
  494. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/projections/__init__.py +0 -0
  495. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/projections/index_ops_store.py +0 -0
  496. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/projections/vector_index_store.py +0 -0
  497. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/raw/__init__.py +0 -0
  498. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/raw/browser_flat_tables.py +0 -0
  499. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/raw/file_store.py +0 -0
  500. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/raw/raw_store.py +0 -0
  501. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/raw/raw_tables_manager.py +0 -0
  502. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/raw/sqlite_raw_store.py +0 -0
  503. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/security/encryption.py +0 -0
  504. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/signal_identity.py +0 -0
  505. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/source_settings.py +0 -0
  506. {topos_node-1.2.0 → topos_node-1.2.1}/topos/storage/user_identity.py +0 -0
  507. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sync/__init__.py +0 -0
  508. {topos_node-1.2.0 → topos_node-1.2.1}/topos/sync_handlers.py +0 -0
  509. {topos_node-1.2.0 → topos_node-1.2.1}/topos/testing/__init__.py +0 -0
  510. {topos_node-1.2.0 → topos_node-1.2.1}/topos/testing/lifespan.py +0 -0
  511. {topos_node-1.2.0 → topos_node-1.2.1}/topos/uma_contact_enrichment.py +0 -0
  512. {topos_node-1.2.0 → topos_node-1.2.1}/topos/uma_filters.py +0 -0
  513. {topos_node-1.2.0 → topos_node-1.2.1}/topos/uma_resource_id.py +0 -0
  514. {topos_node-1.2.0 → topos_node-1.2.1}/topos/uma_rpt.py +0 -0
  515. {topos_node-1.2.0 → topos_node-1.2.1}/topos/upgrades/__init__.py +0 -0
  516. {topos_node-1.2.0 → topos_node-1.2.1}/topos/upgrades/manifests.json +0 -0
  517. {topos_node-1.2.0 → topos_node-1.2.1}/topos/utils/base_object.py +0 -0
  518. {topos_node-1.2.0 → topos_node-1.2.1}/topos/websocket_client.py +0 -0
  519. {topos_node-1.2.0 → topos_node-1.2.1}/topos_node.egg-info/dependency_links.txt +0 -0
  520. {topos_node-1.2.0 → topos_node-1.2.1}/topos_node.egg-info/entry_points.txt +0 -0
  521. {topos_node-1.2.0 → topos_node-1.2.1}/topos_node.egg-info/requires.txt +0 -0
  522. {topos_node-1.2.0 → topos_node-1.2.1}/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.2.0
3
+ Version: 1.2.1
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.2.0"
7
+ version = "1.2.1"
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"
@@ -1,6 +1,6 @@
1
1
  """Topos Control Plane Version Information."""
2
2
 
3
- __version__ = "1.2.0"
4
- __version_info__ = (1, 2, 0)
3
+ __version__ = "1.2.1"
4
+ __version_info__ = (1, 2, 1)
5
5
 
6
6
  __all__ = ["__version__", "__version_info__"]
@@ -672,7 +672,7 @@ def _load_canonical_messages_for_enrichment(
672
672
  if title:
673
673
  rec["content"] = str(title)
674
674
  if not rec.get("event_at"):
675
- for key in ("occurred_at", "entry_at", "starts_at", "posted_at", "visited_at"):
675
+ for key in ("occurred_at", "starts_at", "entry_at", "posted_at", "visited_at"):
676
676
  if rec.get(key):
677
677
  rec["event_at"] = rec[key]
678
678
  break
@@ -1056,6 +1056,15 @@ async def _process_enrichment_core(
1056
1056
  progress_callback=progress_callback,
1057
1057
  )
1058
1058
 
1059
+ # Canonical jobs changed the derived layers even when the signal lane is
1060
+ # skipped below — mark the graph for a debounced rebuild either way.
1061
+ try:
1062
+ from ..features.entities.graph_refresh import mark_graph_dirty
1063
+
1064
+ mark_graph_dirty()
1065
+ except Exception: # noqa: BLE001 — refresh must never break enrichment
1066
+ pass
1067
+
1059
1068
  # Manual processing is the deliberate counterpart of the automatic ingest
1060
1069
  # path, so it must also run the signal-derivation lane (embeddings,
1061
1070
  # clusters, facts, ...) that the ingest path skips for manual sources.
@@ -388,6 +388,29 @@ async def dismiss_entity_review(review_id: str, _api_key: str = Depends(require_
388
388
  raise HTTPException(status_code=400, detail=str(exc))
389
389
 
390
390
 
391
+ class EntitySplitBody(BaseModel):
392
+ surface: str = Field(..., min_length=1, max_length=200)
393
+
394
+
395
+ @router.post("/entities/{entity_id}/split")
396
+ async def split_entity_surface(
397
+ entity_id: str,
398
+ body: EntitySplitBody,
399
+ _api_key: str = Depends(require_api_key),
400
+ ):
401
+ """Owner unbind: split a surface's mentions out of this entity into a
402
+ fresh one, with a permanent no-bind guard so the resolver never re-merges
403
+ the pair (accidental-merge reversal; see consolidation.split_surface)."""
404
+ from ..features.entities.consolidation import split_surface
405
+
406
+ try:
407
+ return split_surface(_entities_conn(), entity_id, body.surface)
408
+ except LookupError as exc:
409
+ raise HTTPException(status_code=404, detail=str(exc))
410
+ except ValueError as exc:
411
+ raise HTTPException(status_code=400, detail=str(exc))
412
+
413
+
391
414
  @router.post("/entities/{entity_id}/exclude")
392
415
  async def exclude_entity_from_intelligence(
393
416
  entity_id: str,
@@ -0,0 +1,30 @@
1
+ """Upgrade status surface: what the runner + graph refresher are doing.
2
+
3
+ Read-only. The UI shows "Upgrading your intelligence…" instead of a
4
+ mysteriously thin graph while re-derivation runs.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Any, Dict
10
+
11
+ from fastapi import APIRouter, Depends
12
+
13
+ from ..auth import require_api_key
14
+
15
+ router = APIRouter()
16
+
17
+
18
+ def _status_payload() -> Dict[str, Any]:
19
+ from ..core.state import get_db_connection
20
+ from ..features.entities.graph_refresh import status as refresh_status
21
+ from ..upgrades.runner import runner_status
22
+
23
+ conn = get_db_connection()
24
+ upgrade = runner_status(conn) if conn is not None else {"enabled": False, "error": "no database"}
25
+ return {"upgrade": upgrade, "graph_refresh": refresh_status()}
26
+
27
+
28
+ @router.get("/v1/upgrade/status", dependencies=[Depends(require_api_key)])
29
+ async def get_upgrade_status() -> Dict[str, Any]:
30
+ return _status_payload()
@@ -45,6 +45,7 @@ from .api import (
45
45
  home_chat as home_chat_routes,
46
46
  privacy_disclose as privacy_disclose_routes,
47
47
  signal as signal_routes,
48
+ upgrades as upgrades_routes,
48
49
  )
49
50
  from .config.settings import settings
50
51
  from .core.logging import align_uvicorn_loggers, configure_logging
@@ -93,6 +94,7 @@ app.add_middleware(
93
94
  )
94
95
 
95
96
  app.include_router(health_routes.router)
97
+ app.include_router(upgrades_routes.router)
96
98
  app.include_router(local_mcp_routes.router)
97
99
  app.include_router(llm_routes.router)
98
100
  app.include_router(db_routes.router)
@@ -157,6 +159,19 @@ async def startup_event() -> None:
157
159
  logger.info("Stage 9 migrations applied at startup: %d renames", len(result["applied"]))
158
160
  except Exception as e:
159
161
  logger.debug("Stage 9 migrations at startup (non-fatal): %s", e)
162
+ # Upgrade runner: compare the stamped derivation baseline against the
163
+ # shipped version and re-derive what this release invalidated (manifest in
164
+ # topos/upgrades). Heavy steps run in a daemon thread; the node serves
165
+ # stale derived data meanwhile. Fresh installs stamp-and-skip.
166
+ try:
167
+ from .core.state import get_db_connection as _get_conn_for_upgrades
168
+ from .upgrades.runner import start_background as _start_upgrades
169
+
170
+ _upgrade_conn = state.db_conn if state.db_conn is not None else _get_conn_for_upgrades()
171
+ if _upgrade_conn is not None:
172
+ _start_upgrades(_upgrade_conn)
173
+ except Exception as e:
174
+ logger.warning("Upgrade runner at startup failed (non-fatal): %s", e)
160
175
  try:
161
176
  from .sources import install_service
162
177
 
@@ -141,7 +141,8 @@ class ControlPlaneClient:
141
141
  try:
142
142
  await asyncio.wait_for(self._ready.wait(), timeout=max(0.1, timeout))
143
143
  return True
144
- except TimeoutError:
144
+ except (TimeoutError, asyncio.TimeoutError):
145
+ # Py3.10: asyncio.TimeoutError is not builtins.TimeoutError.
145
146
  return False
146
147
 
147
148
  async def stop(self) -> None:
@@ -253,7 +254,10 @@ class ControlPlaneClient:
253
254
  async def _wait_for_stop_or_timeout(self, timeout_s: float) -> None:
254
255
  try:
255
256
  await asyncio.wait_for(self._stop.wait(), timeout=timeout_s)
256
- except TimeoutError:
257
+ except (TimeoutError, asyncio.TimeoutError):
258
+ # Expected path: backoff elapsed. Must not escape — on Py3.10
259
+ # asyncio.TimeoutError is distinct from builtins.TimeoutError and
260
+ # would otherwise kill the reconnect loop task.
257
261
  return
258
262
 
259
263
  async def _schedule_inbound_message(self, ws, data: Dict[str, Any]) -> None:
@@ -349,6 +349,20 @@ async def handle_delete_signal_extraction_config(message: Dict[str, Any]) -> Opt
349
349
  # ---- Exposure-profile visibility (P1.5): the owner's toggle for whether the
350
350
  # labeled "what you've been exposed to" profile surfaces on interest/identity
351
351
  # answers. Default ON; the engine read path is config.settings.exposure_profile_visible.
352
+ @handles("get_upgrade_status")
353
+ async def handle_get_upgrade_status(message):
354
+ """Upgrade runner + graph-refresh status (read-only; UI progress banner)."""
355
+ req_id = message.get("id")
356
+ if not req_id:
357
+ return None
358
+ try:
359
+ from ...api.upgrades import _status_payload
360
+
361
+ return {"id": req_id, "status": "ok", "payload": _status_payload()}
362
+ except Exception as exc: # noqa: BLE001
363
+ return {"id": req_id, "status": "error", "error": str(exc)}
364
+
365
+
352
366
  @handles("get_exposure_profile_config")
353
367
  async def handle_get_exposure_profile_config(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
354
368
  req_id = message.get("id")
@@ -651,6 +651,29 @@ async def handle_signal_entity_review_action(message: Dict[str, Any]) -> Optiona
651
651
  return {"id": req_id, "status": "error", "error": str(exc)}
652
652
 
653
653
 
654
+ @handles("signal_entity_split")
655
+ async def handle_signal_entity_split(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
656
+ req_id = message.get("id")
657
+ if not req_id:
658
+ return None
659
+ payload = message.get("payload") or {}
660
+ entity_id = str(payload.get("entity_id") or "").strip()
661
+ surface = str(payload.get("surface") or "").strip()
662
+ if not entity_id or not surface:
663
+ return {"id": req_id, "status": "error", "error": "entity_id and surface required", "code": 400}
664
+ try:
665
+ from ...features.entities.consolidation import split_surface
666
+
667
+ conn = hub.get_db_connection()
668
+ return {"id": req_id, "status": "ok", "payload": split_surface(conn, entity_id, surface)}
669
+ except LookupError as exc:
670
+ return {"id": req_id, "status": "error", "error": str(exc), "code": 404}
671
+ except ValueError as exc:
672
+ return {"id": req_id, "status": "error", "error": str(exc), "code": 400}
673
+ except Exception as exc: # noqa: BLE001
674
+ return {"id": req_id, "status": "error", "error": str(exc)}
675
+
676
+
654
677
  @handles("signal_exclude_entity")
655
678
  async def handle_signal_exclude_entity(message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
656
679
  req_id = message.get("id")
@@ -18,6 +18,7 @@ Dismissed pairs are never re-proposed.
18
18
 
19
19
  from __future__ import annotations
20
20
 
21
+ import json
21
22
  import logging
22
23
  import sqlite3
23
24
  import uuid
@@ -386,3 +387,100 @@ def resolve_review(
386
387
  conn.commit()
387
388
  refresh_dossiers(conn)
388
389
  return {"review_id": review_id, "status": "approved", "kept": str(candidate_id), "absorbed": str(subject_id)}
390
+
391
+
392
+ def split_surface(conn: sqlite3.Connection, entity_id: str, surface: str) -> Dict[str, Any]:
393
+ """Owner unbind: split a surface's mentions OUT of an entity, permanently.
394
+
395
+ The reverse of an accidental merge/bind (e.g. the resolver's unique-contact
396
+ tier binding "Claire" to the contact "Claire Duncombe"):
397
+
398
+ * mentions whose surface_text normalizes to ``surface`` move to a fresh
399
+ entity named after the surface (created only when mentions exist);
400
+ * a matching alias is removed from the source entity;
401
+ * a permanent ``no_bind`` guard row is written — the resolver consults it
402
+ so this surface can never resolve back to the source entity (token-
403
+ subset names score 1.0, so every tier would otherwise re-merge them);
404
+ * both mention counts recount. Edges/dossiers correct on the next graph
405
+ rebuild.
406
+ """
407
+ from .resolver import EntityResolver, normalize_name
408
+
409
+ row = conn.execute(
410
+ "SELECT canonical_name, normalized_name, entity_type, aliases_json "
411
+ "FROM entities WHERE entity_id=?",
412
+ (str(entity_id),),
413
+ ).fetchone()
414
+ if row is None:
415
+ raise LookupError(f"entity not found: {entity_id}")
416
+ canonical_name, normalized_name, entity_type, aliases_json = row
417
+
418
+ normalized = normalize_name(surface)
419
+ if not normalized:
420
+ raise ValueError(f"unresolvable surface: {surface!r}")
421
+ if normalized == str(normalized_name):
422
+ raise ValueError(
423
+ "surface is the entity's own name — use exclude to stop tracking it entirely"
424
+ )
425
+
426
+ # Move matching mentions to a fresh entity.
427
+ mention_rows = conn.execute(
428
+ "SELECT mention_id, surface_text FROM entity_mentions WHERE entity_id=?",
429
+ (str(entity_id),),
430
+ ).fetchall()
431
+ moved_ids = [
432
+ str(mid) for mid, stext in mention_rows if normalize_name(str(stext or "")) == normalized
433
+ ]
434
+
435
+ new_entity_id: Optional[str] = None
436
+ if moved_ids:
437
+ resolver = EntityResolver(conn)
438
+ new_entity_id = resolver._create_entity(str(surface).strip(), str(entity_type))
439
+ placeholders = ",".join("?" for _ in moved_ids)
440
+ conn.execute(
441
+ f"UPDATE entity_mentions SET entity_id=? WHERE mention_id IN ({placeholders})",
442
+ (new_entity_id, *moved_ids),
443
+ )
444
+
445
+ # Remove a matching alias from the source entity.
446
+ alias_removed = False
447
+ try:
448
+ aliases = [a for a in (json.loads(aliases_json or "[]")) if isinstance(a, str)]
449
+ except json.JSONDecodeError:
450
+ aliases = []
451
+ kept_aliases = [a for a in aliases if normalize_name(a) != normalized]
452
+ if len(kept_aliases) != len(aliases):
453
+ alias_removed = True
454
+ conn.execute(
455
+ "UPDATE entities SET aliases_json=?, updated_at=datetime('now') WHERE entity_id=?",
456
+ (json.dumps(kept_aliases), str(entity_id)),
457
+ )
458
+
459
+ # Permanent guard: this surface never binds to the source entity again.
460
+ conn.execute(
461
+ """
462
+ INSERT INTO entity_review
463
+ (review_id, surface_text, candidate_entity_id, score, record_id,
464
+ status, created_at, kind, subject_entity_id, reason)
465
+ VALUES (?, ?, ?, 1.0, NULL, 'approved', datetime('now'), 'no_bind', ?, 'owner split')
466
+ """,
467
+ (f"rev_{uuid.uuid4().hex[:16]}", normalized, str(entity_id), new_entity_id),
468
+ )
469
+
470
+ # Recount both sides.
471
+ for eid in filter(None, [str(entity_id), new_entity_id]):
472
+ conn.execute(
473
+ "UPDATE entities SET mention_count = ("
474
+ " SELECT COUNT(*) FROM entity_mentions m WHERE m.entity_id = entities.entity_id"
475
+ "), updated_at=datetime('now') WHERE entity_id=?",
476
+ (eid,),
477
+ )
478
+ conn.commit()
479
+
480
+ return {
481
+ "entity_id": str(entity_id),
482
+ "surface": str(surface).strip(),
483
+ "new_entity_id": new_entity_id,
484
+ "mentions_moved": len(moved_ids),
485
+ "alias_removed": alias_removed,
486
+ }
@@ -100,11 +100,14 @@ def _upsert_materialized_edge(
100
100
  source_object_id: str,
101
101
  asserted_by: Optional[str] = None,
102
102
  actor_role: Optional[str] = None,
103
+ last_event_at: Optional[str] = None,
103
104
  ) -> None:
104
105
  """Idempotent directed edge with a materialized marker + carried validity.
105
106
 
106
107
  ``actor_role`` overrides the asserted_by-derived tier — used by enrichers
107
108
  whose provenance isn't an assertion (presence, participation, witnessing).
109
+ ``last_event_at`` defaults to valid_from — pass explicitly when the edge's
110
+ evidence spans a window (e.g. a goal recurring across months).
108
111
  """
109
112
  if not src or not dst or src == dst:
110
113
  return
@@ -126,6 +129,7 @@ def _upsert_materialized_edge(
126
129
  """,
127
130
  (src, dst, edge_type),
128
131
  ).fetchone()
132
+ effective_last = last_event_at or valid_from
129
133
  if row is None:
130
134
  conn.execute(
131
135
  """
@@ -137,17 +141,18 @@ def _upsert_materialized_edge(
137
141
  (
138
142
  f"fmz_{uuid.uuid4().hex[:16]}",
139
143
  src, dst, edge_type, float(weight),
140
- valid_from, valid_from or _now_iso(), valid_to, meta,
144
+ effective_last, valid_from or _now_iso(), valid_to, meta,
141
145
  ),
142
146
  )
143
147
  else:
144
148
  conn.execute(
145
149
  """
146
150
  UPDATE entity_edges
147
- SET weight=?, valid_from=?, valid_to=?, metadata_json=?, updated_at=datetime('now')
151
+ SET weight=?, valid_from=?, valid_to=?, last_event_at=?, metadata_json=?,
152
+ updated_at=datetime('now')
148
153
  WHERE edge_id=?
149
154
  """,
150
- (float(weight), valid_from, valid_to, meta, row[0]),
155
+ (float(weight), valid_from, valid_to, effective_last, meta, row[0]),
151
156
  )
152
157
 
153
158