topos-node 0.1.6__tar.gz → 0.1.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 (255) hide show
  1. {topos_node-0.1.6 → topos_node-0.1.8}/PKG-INFO +1 -1
  2. {topos_node-0.1.6 → topos_node-0.1.8}/pyproject.toml +1 -1
  3. {topos_node-0.1.6 → topos_node-0.1.8}/topos/__version__.py +2 -2
  4. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/device.py +2 -2
  5. {topos_node-0.1.6 → topos_node-0.1.8}/topos/control_plane_client.py +19 -0
  6. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/api_models.py +1 -0
  7. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/handlers.py +12 -1
  8. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/state.py +58 -0
  9. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/interfaces.py +1 -1
  10. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/local.py +6 -2
  11. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/postgres.py +68 -5
  12. {topos_node-0.1.6 → topos_node-0.1.8}/topos_node.egg-info/PKG-INFO +1 -1
  13. {topos_node-0.1.6 → topos_node-0.1.8}/LICENSE +0 -0
  14. {topos_node-0.1.6 → topos_node-0.1.8}/README.md +0 -0
  15. {topos_node-0.1.6 → topos_node-0.1.8}/setup.cfg +0 -0
  16. {topos_node-0.1.6 → topos_node-0.1.8}/shared/__init__.py +0 -0
  17. {topos_node-0.1.6 → topos_node-0.1.8}/shared/filtering.py +0 -0
  18. {topos_node-0.1.6 → topos_node-0.1.8}/shared/schema_registry.py +0 -0
  19. {topos_node-0.1.6 → topos_node-0.1.8}/topos/__init__.py +0 -0
  20. {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/__init__.py +0 -0
  21. {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/duckdb_adapter.py +0 -0
  22. {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/messenger_communities.py +0 -0
  23. {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/messenger_graph.py +0 -0
  24. {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/messenger_labels.py +0 -0
  25. {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/profiles.py +0 -0
  26. {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/query_engine.py +0 -0
  27. {topos_node-0.1.6 → topos_node-0.1.8}/topos/analytics/raw_queries.py +0 -0
  28. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/__init__.py +0 -0
  29. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/analytics.py +0 -0
  30. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/app_registry.py +0 -0
  31. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/backup.py +0 -0
  32. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/compute_remote.py +0 -0
  33. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/data_commit.py +0 -0
  34. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/data_explorer_table_prefs.py +0 -0
  35. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/db.py +0 -0
  36. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/enrichment.py +0 -0
  37. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/filter_lab.py +0 -0
  38. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/health.py +0 -0
  39. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/ingestion_api.py +0 -0
  40. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/ingestion_compat.py +0 -0
  41. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/ingestion_sources.py +0 -0
  42. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/llm.py +0 -0
  43. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/local_mcp.py +0 -0
  44. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/messenger_analytics.py +0 -0
  45. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/query_api.py +0 -0
  46. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/sanitization_ollama_config.py +0 -0
  47. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/source_install.py +0 -0
  48. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/sources.py +0 -0
  49. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/sync.py +0 -0
  50. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/ui_config.py +0 -0
  51. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/uma_data.py +0 -0
  52. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/usage.py +0 -0
  53. {topos_node-0.1.6 → topos_node-0.1.8}/topos/api/user_identity.py +0 -0
  54. {topos_node-0.1.6 → topos_node-0.1.8}/topos/app.py +0 -0
  55. {topos_node-0.1.6 → topos_node-0.1.8}/topos/auth.py +0 -0
  56. {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/__init__.py +0 -0
  57. {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/mappers/__init__.py +0 -0
  58. {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/mappers/base.py +0 -0
  59. {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/mappers/chatgpt_mapper.py +0 -0
  60. {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/mappers/grok_mapper.py +0 -0
  61. {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/mappers/messenger_mapper.py +0 -0
  62. {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/models.py +0 -0
  63. {topos_node-0.1.6 → topos_node-0.1.8}/topos/canonicalization/resolver.py +0 -0
  64. {topos_node-0.1.6 → topos_node-0.1.8}/topos/cli/__init__.py +0 -0
  65. {topos_node-0.1.6 → topos_node-0.1.8}/topos/cli/__main__.py +0 -0
  66. {topos_node-0.1.6 → topos_node-0.1.8}/topos/cli/commands.py +0 -0
  67. {topos_node-0.1.6 → topos_node-0.1.8}/topos/config/__init__.py +0 -0
  68. {topos_node-0.1.6 → topos_node-0.1.8}/topos/config/sanitization_ollama.py +0 -0
  69. {topos_node-0.1.6 → topos_node-0.1.8}/topos/config/settings.py +0 -0
  70. {topos_node-0.1.6 → topos_node-0.1.8}/topos/contacts/__init__.py +0 -0
  71. {topos_node-0.1.6 → topos_node-0.1.8}/topos/contacts/identity.py +0 -0
  72. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/__init__.py +0 -0
  73. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/connection_resilience.py +0 -0
  74. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/device_helpers.py +0 -0
  75. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/errors.py +0 -0
  76. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/events.py +0 -0
  77. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/logging.py +0 -0
  78. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/metrics.py +0 -0
  79. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/startup_banner.py +0 -0
  80. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/table_layers.py +0 -0
  81. {topos_node-0.1.6 → topos_node-0.1.8}/topos/core/types.py +0 -0
  82. {topos_node-0.1.6 → topos_node-0.1.8}/topos/data_explorer_table_prefs.py +0 -0
  83. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/__init__.py +0 -0
  84. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/backends/__init__.py +0 -0
  85. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/backends/base.py +0 -0
  86. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/backends/huggingface.py +0 -0
  87. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/backends/ollama.py +0 -0
  88. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/backends/stub.py +0 -0
  89. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/engine.py +0 -0
  90. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/intake.py +0 -0
  91. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/queue_manager.py +0 -0
  92. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/registration.py +0 -0
  93. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/result_formatter.py +0 -0
  94. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/router.py +0 -0
  95. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/scoped_token.py +0 -0
  96. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/tasks.py +0 -0
  97. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/transport.py +0 -0
  98. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/usage_guard.py +0 -0
  99. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/usage_observation.py +0 -0
  100. {topos_node-0.1.6 → topos_node-0.1.8}/topos/engine/validator.py +0 -0
  101. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/__init__.py +0 -0
  102. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/derived_tables.py +0 -0
  103. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/__init__.py +0 -0
  104. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/base.py +0 -0
  105. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/__init__.py +0 -0
  106. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/embeddings_job.py +0 -0
  107. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/emo_27_job.py +0 -0
  108. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/entities_job.py +0 -0
  109. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/sentiment_job.py +0 -0
  110. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/canonical/topics_job.py +0 -0
  111. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/raw/__init__.py +0 -0
  112. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/raw/attachments_job.py +0 -0
  113. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/raw/language_job.py +0 -0
  114. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/raw/time_normalization_job.py +0 -0
  115. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/jobs/raw/tool_calls_job.py +0 -0
  116. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/models/__init__.py +0 -0
  117. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/models/manager.py +0 -0
  118. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/models/registry.py +0 -0
  119. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/models/versioning.py +0 -0
  120. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/orchestrator.py +0 -0
  121. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/processor.py +0 -0
  122. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/progress_bar.py +0 -0
  123. {topos_node-0.1.6 → topos_node-0.1.8}/topos/enrichment/website_classifier.py +0 -0
  124. {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/__init__.py +0 -0
  125. {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/bundles.py +0 -0
  126. {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/schema.py +0 -0
  127. {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/service.py +0 -0
  128. {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/store.py +0 -0
  129. {topos_node-0.1.6 → topos_node-0.1.8}/topos/filter_lab/worker.py +0 -0
  130. {topos_node-0.1.6 → topos_node-0.1.8}/topos/hosted_pool_lease.py +0 -0
  131. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/__init__.py +0 -0
  132. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/checkpoints/__init__.py +0 -0
  133. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/checkpoints/checkpoint_store.py +0 -0
  134. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/checkpoints/sqlite_checkpoint_store.py +0 -0
  135. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/ingest_helpers.py +0 -0
  136. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/jobs.py +0 -0
  137. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/local_sync.py +0 -0
  138. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/log_preview.py +0 -0
  139. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/manager.py +0 -0
  140. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parser.py +0 -0
  141. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/__init__.py +0 -0
  142. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/base.py +0 -0
  143. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/browser_parser.py +0 -0
  144. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/calendar_parser.py +0 -0
  145. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/chatgpt_conversation_flattener.py +0 -0
  146. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/chatgpt_parser.py +0 -0
  147. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/grok_parser.py +0 -0
  148. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/parsers/messenger_parser.py +0 -0
  149. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/progress.py +0 -0
  150. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/__init__.py +0 -0
  151. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/base.py +0 -0
  152. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/calendar.py +0 -0
  153. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/chatgpt.py +0 -0
  154. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/contact_importers.py +0 -0
  155. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/grok.py +0 -0
  156. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/imessage_reader.py +0 -0
  157. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/signal_export_parser.py +0 -0
  158. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/sources/signal_reader.py +0 -0
  159. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/state_machine.py +0 -0
  160. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/triggers/__init__.py +0 -0
  161. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/triggers/file_trigger.py +0 -0
  162. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/triggers/sqlite_trigger.py +0 -0
  163. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/validation/__init__.py +0 -0
  164. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/validation/base.py +0 -0
  165. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/validation/schema_registry.py +0 -0
  166. {topos_node-0.1.6 → topos_node-0.1.8}/topos/ingestion/validation/schema_validator.py +0 -0
  167. {topos_node-0.1.6 → topos_node-0.1.8}/topos/lineage/__init__.py +0 -0
  168. {topos_node-0.1.6 → topos_node-0.1.8}/topos/lineage/provenance.py +0 -0
  169. {topos_node-0.1.6 → topos_node-0.1.8}/topos/lineage/tracker.py +0 -0
  170. {topos_node-0.1.6 → topos_node-0.1.8}/topos/mcp_stdio_proxy.py +0 -0
  171. {topos_node-0.1.6 → topos_node-0.1.8}/topos/observability/__init__.py +0 -0
  172. {topos_node-0.1.6 → topos_node-0.1.8}/topos/observability/alerts.py +0 -0
  173. {topos_node-0.1.6 → topos_node-0.1.8}/topos/observability/metrics.py +0 -0
  174. {topos_node-0.1.6 → topos_node-0.1.8}/topos/observability/tracing.py +0 -0
  175. {topos_node-0.1.6 → topos_node-0.1.8}/topos/openai_client.py +0 -0
  176. {topos_node-0.1.6 → topos_node-0.1.8}/topos/projections/__init__.py +0 -0
  177. {topos_node-0.1.6 → topos_node-0.1.8}/topos/projections/vector_index/__init__.py +0 -0
  178. {topos_node-0.1.6 → topos_node-0.1.8}/topos/projections/vector_index/base.py +0 -0
  179. {topos_node-0.1.6 → topos_node-0.1.8}/topos/projections/vector_index/builders.py +0 -0
  180. {topos_node-0.1.6 → topos_node-0.1.8}/topos/projections/vector_index/health_checks.py +0 -0
  181. {topos_node-0.1.6 → topos_node-0.1.8}/topos/rate_limit.py +0 -0
  182. {topos_node-0.1.6 → topos_node-0.1.8}/topos/sanitization/__init__.py +0 -0
  183. {topos_node-0.1.6 → topos_node-0.1.8}/topos/sanitization/ollama_transforms.py +0 -0
  184. {topos_node-0.1.6 → topos_node-0.1.8}/topos/scope_resolution.py +0 -0
  185. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/__init__.py +0 -0
  186. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/container.py +0 -0
  187. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/embeddings/__init__.py +0 -0
  188. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/embeddings/base.py +0 -0
  189. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/embeddings/local.py +0 -0
  190. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/embeddings/remote.py +0 -0
  191. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/llm/__init__.py +0 -0
  192. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/llm/base.py +0 -0
  193. {topos_node-0.1.6 → topos_node-0.1.8}/topos/services/llm/openai.py +0 -0
  194. {topos_node-0.1.6 → topos_node-0.1.8}/topos/sources/__init__.py +0 -0
  195. {topos_node-0.1.6 → topos_node-0.1.8}/topos/sources/definitions.py +0 -0
  196. {topos_node-0.1.6 → topos_node-0.1.8}/topos/sources/install_service.py +0 -0
  197. {topos_node-0.1.6 → topos_node-0.1.8}/topos/sources/registry.py +0 -0
  198. {topos_node-0.1.6 → topos_node-0.1.8}/topos/sources/runtime_install.py +0 -0
  199. {topos_node-0.1.6 → topos_node-0.1.8}/topos/startup_banner.py +0 -0
  200. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/__init__.py +0 -0
  201. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/__init__.py +0 -0
  202. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/ai_chat/__init__.py +0 -0
  203. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/ai_chat/canonicalizer.py +0 -0
  204. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/ai_chat/mapper.py +0 -0
  205. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/ai_chat/model.py +0 -0
  206. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/ai_chat/tables.py +0 -0
  207. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/canonical_store.py +0 -0
  208. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/conversations_tables.py +0 -0
  209. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/mapping_store.py +0 -0
  210. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/canonical/postgres.py +0 -0
  211. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/__init__.py +0 -0
  212. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/client.py +0 -0
  213. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/migrations/__init__.py +0 -0
  214. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/migrations/stage9_column_renames.py +0 -0
  215. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/paths.py +0 -0
  216. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/postgres.py +0 -0
  217. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/db/schema.py +0 -0
  218. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/enrichment/__init__.py +0 -0
  219. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/enrichment/canonical_enrichment_store.py +0 -0
  220. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/enrichment/raw_enrichment_store.py +0 -0
  221. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/normalized/__init__.py +0 -0
  222. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/normalized/normalized_store.py +0 -0
  223. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/oplog/__init__.py +0 -0
  224. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/oplog/decision.py +0 -0
  225. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/oplog/oplog_store.py +0 -0
  226. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/oplog/postgres.py +0 -0
  227. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/projections/__init__.py +0 -0
  228. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/projections/index_ops_store.py +0 -0
  229. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/projections/vector_index_store.py +0 -0
  230. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/__init__.py +0 -0
  231. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/browser_flat_tables.py +0 -0
  232. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/file_store.py +0 -0
  233. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/raw_store.py +0 -0
  234. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/raw_tables_manager.py +0 -0
  235. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/raw/sqlite_raw_store.py +0 -0
  236. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/security/encryption.py +0 -0
  237. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/signal_identity.py +0 -0
  238. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/source_settings.py +0 -0
  239. {topos_node-0.1.6 → topos_node-0.1.8}/topos/storage/user_identity.py +0 -0
  240. {topos_node-0.1.6 → topos_node-0.1.8}/topos/sync/__init__.py +0 -0
  241. {topos_node-0.1.6 → topos_node-0.1.8}/topos/sync/client.py +0 -0
  242. {topos_node-0.1.6 → topos_node-0.1.8}/topos/sync_handlers.py +0 -0
  243. {topos_node-0.1.6 → topos_node-0.1.8}/topos/testing/__init__.py +0 -0
  244. {topos_node-0.1.6 → topos_node-0.1.8}/topos/testing/lifespan.py +0 -0
  245. {topos_node-0.1.6 → topos_node-0.1.8}/topos/uma_contact_enrichment.py +0 -0
  246. {topos_node-0.1.6 → topos_node-0.1.8}/topos/uma_filters.py +0 -0
  247. {topos_node-0.1.6 → topos_node-0.1.8}/topos/uma_resource_id.py +0 -0
  248. {topos_node-0.1.6 → topos_node-0.1.8}/topos/uma_rpt.py +0 -0
  249. {topos_node-0.1.6 → topos_node-0.1.8}/topos/utils/base_object.py +0 -0
  250. {topos_node-0.1.6 → topos_node-0.1.8}/topos/websocket_client.py +0 -0
  251. {topos_node-0.1.6 → topos_node-0.1.8}/topos_node.egg-info/SOURCES.txt +0 -0
  252. {topos_node-0.1.6 → topos_node-0.1.8}/topos_node.egg-info/dependency_links.txt +0 -0
  253. {topos_node-0.1.6 → topos_node-0.1.8}/topos_node.egg-info/entry_points.txt +0 -0
  254. {topos_node-0.1.6 → topos_node-0.1.8}/topos_node.egg-info/requires.txt +0 -0
  255. {topos_node-0.1.6 → topos_node-0.1.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: 0.1.6
3
+ Version: 0.1.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 = "0.1.6"
7
+ version = "0.1.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"
@@ -1,6 +1,6 @@
1
1
  """Topos Control Plane Version Information."""
2
2
 
3
- __version__ = "0.1.6"
4
- __version_info__ = (0, 1, 6)
3
+ __version__ = "0.1.8"
4
+ __version_info__ = (0, 1, 8)
5
5
 
6
6
  __all__ = ["__version__", "__version_info__"]
@@ -12,12 +12,12 @@ router = APIRouter()
12
12
 
13
13
  @router.get("/device/info", response_model=DeviceInfoResponse, dependencies=[Depends(require_api_key)])
14
14
  async def get_device_info(services: Services = Depends(get_services)):
15
- return await services.device.get_device_info()
15
+ return await services.device.get_device_info(context=None)
16
16
 
17
17
 
18
18
  @router.get("/device_info", response_model=DeviceInfoResponse, dependencies=[Depends(require_api_key)])
19
19
  async def get_device_info_alias(services: Services = Depends(get_services)):
20
- return await services.device.get_device_info()
20
+ return await services.device.get_device_info(context=None)
21
21
 
22
22
 
23
23
  @router.post("/device_name", response_model=DeviceNameResponse, dependencies=[Depends(require_api_key)])
@@ -103,6 +103,24 @@ class ControlPlaneClient:
103
103
  self._task = asyncio.create_task(self._run())
104
104
  logger.info("Control plane client starting: %s", self.control_plane_url)
105
105
 
106
+ def _restart_if_task_stopped(self) -> None:
107
+ # Self-heal if the reconnect loop exited unexpectedly while the app is still running.
108
+ if self._stop.is_set() or not self._task or not self._task.done():
109
+ return
110
+ reason = "unknown"
111
+ try:
112
+ exc = self._task.exception()
113
+ if exc is not None:
114
+ reason = repr(exc)
115
+ except asyncio.CancelledError:
116
+ reason = "cancelled"
117
+ logger.warning(
118
+ "Control plane background task stopped unexpectedly; restarting endpoint=%s reason=%s",
119
+ self.control_plane_url,
120
+ reason,
121
+ )
122
+ self.start()
123
+
106
124
  async def wait_until_connected(self, timeout_s: float | None = None) -> bool:
107
125
  timeout = float(timeout_s) if timeout_s is not None else float(settings.connection_readiness_timeout_seconds)
108
126
  try:
@@ -292,6 +310,7 @@ class ControlPlaneClient:
292
310
  async def send_message(self, message: Dict[str, Any]) -> None:
293
311
  """Send an unsolicited message to the control plane (e.g., progress updates)."""
294
312
  if not self._ws:
313
+ self._restart_if_task_stopped()
295
314
  await self._enqueue_presence_message(message)
296
315
  logger.warning("Queued presence message; control plane currently disconnected")
297
316
  return
@@ -95,6 +95,7 @@ class DeviceInfoResponse(BaseModel):
95
95
  engine_mode: str
96
96
  llm_enabled: bool
97
97
  database_mode: str
98
+ database_version: Optional[str] = None
98
99
  engine_name: Optional[str]
99
100
  engine_version: Optional[str] = None
100
101
  system: Dict[str, Any]
@@ -1139,6 +1139,15 @@ async def handle_control_plane_request(message: Dict[str, Any]) -> Optional[Dict
1139
1139
  _payload = message.get("payload") or {}
1140
1140
  _mcp_source = _payload.get("mcp_source")
1141
1141
  _mcp_requester_id = _payload.get("mcp_requester_id")
1142
+ if msg_type == "migrate_browser_plugin_app_id":
1143
+ from .state import _migrate_legacy_browser_plugin_app_ids
1144
+
1145
+ conn = get_db_connection()
1146
+ if not conn:
1147
+ return {"id": req_id, "status": "error", "error": "Database not available"}
1148
+ updated = _migrate_legacy_browser_plugin_app_ids(conn)
1149
+ return {"id": req_id, "status": "ok", "payload": {"updated_rows": updated}}
1150
+
1142
1151
  if msg_type == "get_request_counts":
1143
1152
  """Return UMA + MCP request counts from engine DB (for CP proxy or direct frontend)."""
1144
1153
  payload = message.get("payload") or {}
@@ -1759,8 +1768,10 @@ async def handle_control_plane_request(message: Dict[str, Any]) -> Optional[Dict
1759
1768
  except Exception as exc: # noqa: BLE001
1760
1769
  return {"id": req_id, "status": "error", "error": str(exc)}
1761
1770
  if msg_type == "get_device_info":
1771
+ payload = message.get("payload") or {}
1772
+ context = payload if isinstance(payload, dict) else {}
1762
1773
  try:
1763
- result = await get_services().device.get_device_info()
1774
+ result = await get_services().device.get_device_info(context=context)
1764
1775
  return {"id": req_id, "status": "ok", "payload": result.model_dump()}
1765
1776
  except Exception as exc: # noqa: BLE001
1766
1777
  return {"id": req_id, "status": "error", "error": str(exc)}
@@ -14,6 +14,8 @@ from typing import Any, Dict, Optional
14
14
 
15
15
  MCP_REQUEST_LOG_TABLE = "mcp_request_log"
16
16
  UMA_ACCESS_REQUESTS_TABLE = "uma_access_requests"
17
+ LEGACY_BROWSER_PLUGIN_APP_ID = "browser-plugin"
18
+ BROWSER_HISTORY_PLUGIN_APP_ID = "browser-history-plugin"
17
19
 
18
20
 
19
21
  def derive_uma_access_context(owner_user_id: str, requesting_user_id: Optional[str]) -> str:
@@ -401,6 +403,29 @@ def _ensure_uma_access_requests_table(conn: sqlite3.Connection) -> None:
401
403
  conn.commit()
402
404
  except Exception as exc:
403
405
  logger.warning("Failed to ensure uma_access_requests table exists: %s", exc)
406
+ else:
407
+ _migrate_legacy_browser_plugin_app_ids(conn)
408
+
409
+
410
+ def _migrate_legacy_browser_plugin_app_ids(conn: sqlite3.Connection) -> int:
411
+ """Rewrite engine Activity counts from browser-plugin → browser-history-plugin (idempotent)."""
412
+ try:
413
+ cursor = conn.execute(
414
+ f"UPDATE {UMA_ACCESS_REQUESTS_TABLE} SET app_id = ? WHERE app_id = ?",
415
+ (BROWSER_HISTORY_PLUGIN_APP_ID, LEGACY_BROWSER_PLUGIN_APP_ID),
416
+ )
417
+ if cursor.rowcount:
418
+ conn.commit()
419
+ logger.info(
420
+ "Migrated %s uma_access_requests rows: %s → %s",
421
+ cursor.rowcount,
422
+ LEGACY_BROWSER_PLUGIN_APP_ID,
423
+ BROWSER_HISTORY_PLUGIN_APP_ID,
424
+ )
425
+ return int(cursor.rowcount or 0)
426
+ except Exception as exc:
427
+ logger.warning("browser-plugin app_id migration on engine failed: %s", exc)
428
+ return 0
404
429
 
405
430
 
406
431
  def record_uma_request(
@@ -463,6 +488,7 @@ def get_uma_request_counts(
463
488
  "total_read_requests": 0,
464
489
  "total_write_requests": 0,
465
490
  "by_app": [],
491
+ "by_app_requester": [],
466
492
  "by_requesting_user": [],
467
493
  "access_attribution": {
468
494
  "window_days": since_days or 0,
@@ -510,6 +536,38 @@ def get_uma_request_counts(
510
536
  for aid, d in sorted(by_app.items())
511
537
  ]
512
538
 
539
+ cursor = conn.execute(
540
+ f"""SELECT app_id, requesting_user_id, requesting_user_email, request_type
541
+ FROM {UMA_ACCESS_REQUESTS_TABLE}
542
+ WHERE owner_user_id = ? AND created_at >= ?""",
543
+ (owner_user_id, since_ts),
544
+ )
545
+ by_app_req: Dict[str, Dict[str, Any]] = {}
546
+ for row in cursor.fetchall():
547
+ app_id_val = (row[0] or "").strip() if len(row) > 0 else ""
548
+ rid = (row[1] or "").strip() if len(row) > 1 else ""
549
+ remail = (row[2] or "").strip() if len(row) > 2 else ""
550
+ rt = (row[3] or "").strip().lower() if len(row) > 3 else ""
551
+ key = f"{app_id_val}\0{rid}\0{remail}"
552
+ if key not in by_app_req:
553
+ by_app_req[key] = {
554
+ "app_id": app_id_val or None,
555
+ "requesting_user_id": rid or None,
556
+ "requesting_user_email": remail or None,
557
+ "read_requests": 0,
558
+ "write_requests": 0,
559
+ }
560
+ if rt == "read":
561
+ by_app_req[key]["read_requests"] += 1
562
+ elif rt == "write":
563
+ by_app_req[key]["write_requests"] += 1
564
+ app_req_ranked = []
565
+ for d in by_app_req.values():
566
+ tr = int(d["read_requests"]) + int(d["write_requests"])
567
+ app_req_ranked.append({**d, "total_requests": tr})
568
+ app_req_ranked.sort(key=lambda x: -x["total_requests"])
569
+ out["by_app_requester"] = app_req_ranked[:50]
570
+
513
571
  cursor = conn.execute(
514
572
  f"""SELECT requesting_user_id, requesting_user_email, request_type, access_context
515
573
  FROM {UMA_ACCESS_REQUESTS_TABLE}
@@ -31,7 +31,7 @@ class SyncService(Protocol):
31
31
  class DeviceService(Protocol):
32
32
  async def get_pairing_code(self) -> PairingCodeResponse: ...
33
33
  async def pair_device(self, pairing_code: str, keep_existing_data: bool) -> PairDeviceResponse: ...
34
- async def get_device_info(self) -> DeviceInfoResponse: ...
34
+ async def get_device_info(self, context: Optional[Dict[str, Any]] = None) -> DeviceInfoResponse: ...
35
35
  async def set_device_name(self, device_name: str) -> DeviceNameResponse: ...
36
36
 
37
37
 
@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import sqlite3
3
4
  from typing import Any, Dict, Optional
4
5
 
6
+ from ..__version__ import __version__
5
7
  from ..core.api_models import (
6
8
  DeviceInfoResponse,
7
9
  DeviceNameResponse,
@@ -61,7 +63,8 @@ class LocalDeviceService:
61
63
  _ = (pairing_code, keep_existing_data)
62
64
  raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Pairing not implemented")
63
65
 
64
- async def get_device_info(self) -> DeviceInfoResponse:
66
+ async def get_device_info(self, context: Optional[Dict[str, Any]] = None) -> DeviceInfoResponse:
67
+ _ = context
65
68
  # Get user_id from database (set by connection_info handler) or fall back to settings
66
69
  user_id = None
67
70
  if state.db_conn:
@@ -92,8 +95,9 @@ class LocalDeviceService:
92
95
  engine_mode=state.get_engine_mode(),
93
96
  llm_enabled=settings.enable_llm and state.get_engine_mode() == "full",
94
97
  database_mode=settings.topos_database_mode,
98
+ database_version=sqlite3.sqlite_version if settings.topos_database_mode in {"local", "sqlite"} else None,
95
99
  engine_name=device_name,
96
- engine_version=None,
100
+ engine_version=__version__,
97
101
  system=state.get_system_info(),
98
102
  last_sync_at=last_sync_at,
99
103
  last_received_hlc_ts=last_received_hlc_ts,
@@ -7,7 +7,9 @@ from uuid import uuid4
7
7
 
8
8
  from fastapi import HTTPException, status
9
9
 
10
+ from ..__version__ import __version__
10
11
  from ..config.settings import settings
12
+ from ..core import state
11
13
  from ..core.api_models import (
12
14
  DeviceInfoResponse,
13
15
  DeviceNameResponse,
@@ -22,6 +24,7 @@ from ..storage.db.postgres import (
22
24
  connect_postgres,
23
25
  execute_query,
24
26
  fetch_all,
27
+ fetch_one,
25
28
  )
26
29
 
27
30
 
@@ -364,20 +367,80 @@ class PostgresDbService:
364
367
  }
365
368
 
366
369
 
370
+ def _hosted_postgres_version() -> Optional[str]:
371
+ try:
372
+ with connect_postgres() as conn:
373
+ row = fetch_one(conn, "SELECT version() AS version", ())
374
+ if row and row[0]:
375
+ return str(row[0]).strip() or None
376
+ except Exception:
377
+ return None
378
+ return None
379
+
380
+
381
+ def _hosted_dataset_id(context: Optional[Dict[str, Any]], user_id: Optional[str]) -> Optional[str]:
382
+ ctx = context if isinstance(context, dict) else {}
383
+ explicit = str(ctx.get("dataset_id") or "").strip()
384
+ if explicit:
385
+ return explicit
386
+ if not user_id:
387
+ return None
388
+ tenant_id = str(ctx.get("tenant_id") or "").strip()
389
+ if tenant_id:
390
+ return f"{user_id}:default:{tenant_id}"
391
+ default_dataset = (settings.topos_default_dataset_id or "default").strip() or "default"
392
+ return f"{user_id}:{default_dataset}"
393
+
394
+
367
395
  class HostedDeviceService:
368
396
  async def get_pairing_code(self) -> PairingCodeResponse:
369
- raise NotImplementedError("HostedDeviceService not implemented yet")
397
+ raise HTTPException(
398
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
399
+ detail="Pairing is not available for hosted runtimes",
400
+ )
370
401
 
371
402
  async def pair_device(self, pairing_code: str, keep_existing_data: bool) -> PairDeviceResponse:
372
403
  _ = (pairing_code, keep_existing_data)
373
- raise NotImplementedError("HostedDeviceService not implemented yet")
404
+ raise HTTPException(
405
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
406
+ detail="Pairing is not available for hosted runtimes",
407
+ )
374
408
 
375
- async def get_device_info(self) -> DeviceInfoResponse:
376
- raise NotImplementedError("HostedDeviceService not implemented yet")
409
+ async def get_device_info(self, context: Optional[Dict[str, Any]] = None) -> DeviceInfoResponse:
410
+ ctx = context if isinstance(context, dict) else {}
411
+ user_id = str(ctx.get("owner_user_id") or settings.topos_user_id or "").strip() or None
412
+ dataset_id = _hosted_dataset_id(ctx, user_id)
413
+ engine_mode = state.get_engine_mode()
414
+ device_name = (settings.engine_name or "").strip() or None
415
+
416
+ return DeviceInfoResponse(
417
+ user_id=user_id,
418
+ dataset_id=dataset_id,
419
+ sync_connected=False,
420
+ sync_enabled=False,
421
+ engine_class=state.get_engine_class(),
422
+ engine_mode=engine_mode,
423
+ llm_enabled=bool(settings.enable_llm and engine_mode == "full"),
424
+ database_mode=settings.topos_database_mode or "postgres",
425
+ database_version=_hosted_postgres_version(),
426
+ engine_name=device_name,
427
+ engine_version=__version__,
428
+ system={},
429
+ last_sync_at=None,
430
+ last_received_hlc_ts=None,
431
+ last_received_op_id=None,
432
+ oplog_count=None,
433
+ oplog_bytes=None,
434
+ ops_since_last_sync=None,
435
+ oplog_bytes_since_last_sync=None,
436
+ )
377
437
 
378
438
  async def set_device_name(self, device_name: str) -> DeviceNameResponse:
379
439
  _ = device_name
380
- raise NotImplementedError("HostedDeviceService not implemented yet")
440
+ raise HTTPException(
441
+ status_code=status.HTTP_501_NOT_IMPLEMENTED,
442
+ detail="Device rename is not available for hosted runtimes",
443
+ )
381
444
 
382
445
 
383
446
  class HostedSyncService:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: topos-node
3
- Version: 0.1.6
3
+ Version: 0.1.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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes