topos-node 1.2.2__tar.gz → 1.2.4__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 (530) hide show
  1. {topos_node-1.2.2/topos_node.egg-info → topos_node-1.2.4}/PKG-INFO +1 -1
  2. {topos_node-1.2.2 → topos_node-1.2.4}/pyproject.toml +1 -1
  3. {topos_node-1.2.2 → topos_node-1.2.4}/topos/__version__.py +2 -2
  4. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/enrichment.py +105 -39
  5. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/source_install.py +2 -1
  6. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/ui_config.py +9 -0
  7. {topos_node-1.2.2 → topos_node-1.2.4}/topos/app.py +14 -0
  8. topos_node-1.2.4/topos/canonicalization/mappers/base.py +37 -0
  9. {topos_node-1.2.2 → topos_node-1.2.4}/topos/canonicalization/mappers/github_activity_mapper.py +84 -3
  10. {topos_node-1.2.2 → topos_node-1.2.4}/topos/config/settings.py +7 -2
  11. {topos_node-1.2.2 → topos_node-1.2.4}/topos/control_plane_client.py +42 -0
  12. topos_node-1.2.4/topos/core/handlers/enrichment.py +362 -0
  13. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/ingest.py +86 -133
  14. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/state.py +12 -0
  15. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/backends/ollama.py +17 -3
  16. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/backends/openai_compatible.py +24 -6
  17. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/engine.py +34 -0
  18. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/tasks.py +3 -0
  19. topos_node-1.2.4/topos/engine/usage_observation.py +406 -0
  20. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/catalog.py +4 -0
  21. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/_engine_runner.py +2 -1
  22. topos_node-1.2.4/topos/enrichment/jobs/canonical/timeline_job.py +44 -0
  23. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/edges.py +4 -1
  24. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/graph_refresh.py +55 -0
  25. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/facts/llm_extract.py +29 -1
  26. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/lifecycle/derived_scrub.py +15 -12
  27. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/cluster_labels.py +3 -2
  28. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/stats/definitions.py +10 -1
  29. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/stats/engine.py +2 -0
  30. topos_node-1.2.4/topos/features/timeline_projection.py +353 -0
  31. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/canonical_pipeline.py +39 -7
  32. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/ingest_helpers.py +9 -0
  33. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/local_sync.py +11 -0
  34. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/usage_inbox_dedupe.py +48 -0
  35. {topos_node-1.2.2 → topos_node-1.2.4}/topos/llm_integrations_storage.py +119 -5
  36. topos_node-1.2.4/topos/pipeline/job_runner.py +242 -0
  37. topos_node-1.2.4/topos/pipeline/job_store.py +279 -0
  38. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/scope_registry.json +61 -7
  39. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sources/definitions.py +48 -0
  40. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sources/install_service.py +11 -5
  41. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sources/registry.py +14 -9
  42. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sources/scrub_attribution.py +88 -0
  43. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/conversations_tables.py +13 -3
  44. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/__init__.py +13 -0
  45. topos_node-1.2.4/topos/storage/db/migrations/canonical_address_book_v1.py +103 -0
  46. topos_node-1.2.4/topos/storage/db/migrations/pipeline_jobs_v1.py +75 -0
  47. {topos_node-1.2.2 → topos_node-1.2.4/topos_node.egg-info}/PKG-INFO +1 -1
  48. {topos_node-1.2.2 → topos_node-1.2.4}/topos_node.egg-info/SOURCES.txt +5 -0
  49. topos_node-1.2.2/topos/canonicalization/mappers/base.py +0 -26
  50. topos_node-1.2.2/topos/core/handlers/enrichment.py +0 -582
  51. topos_node-1.2.2/topos/engine/usage_observation.py +0 -129
  52. topos_node-1.2.2/topos/enrichment/jobs/canonical/timeline_job.py +0 -67
  53. {topos_node-1.2.2 → topos_node-1.2.4}/LICENSE +0 -0
  54. {topos_node-1.2.2 → topos_node-1.2.4}/MANIFEST.in +0 -0
  55. {topos_node-1.2.2 → topos_node-1.2.4}/README.md +0 -0
  56. {topos_node-1.2.2 → topos_node-1.2.4}/setup.cfg +0 -0
  57. {topos_node-1.2.2 → topos_node-1.2.4}/shared/__init__.py +0 -0
  58. {topos_node-1.2.2 → topos_node-1.2.4}/shared/filtering.py +0 -0
  59. {topos_node-1.2.2 → topos_node-1.2.4}/shared/schema_registry.py +0 -0
  60. {topos_node-1.2.2 → topos_node-1.2.4}/tests/test_llm_integrations_storage.py +0 -0
  61. {topos_node-1.2.2 → topos_node-1.2.4}/tests/test_protocol_handled_types.py +0 -0
  62. {topos_node-1.2.2 → topos_node-1.2.4}/topos/__init__.py +0 -0
  63. {topos_node-1.2.2 → topos_node-1.2.4}/topos/analytics/__init__.py +0 -0
  64. {topos_node-1.2.2 → topos_node-1.2.4}/topos/analytics/duckdb_adapter.py +0 -0
  65. {topos_node-1.2.2 → topos_node-1.2.4}/topos/analytics/messenger_communities.py +0 -0
  66. {topos_node-1.2.2 → topos_node-1.2.4}/topos/analytics/messenger_graph.py +0 -0
  67. {topos_node-1.2.2 → topos_node-1.2.4}/topos/analytics/messenger_labels.py +0 -0
  68. {topos_node-1.2.2 → topos_node-1.2.4}/topos/analytics/profiles.py +0 -0
  69. {topos_node-1.2.2 → topos_node-1.2.4}/topos/analytics/query_engine.py +0 -0
  70. {topos_node-1.2.2 → topos_node-1.2.4}/topos/analytics/raw_queries.py +0 -0
  71. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/__init__.py +0 -0
  72. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/analytics.py +0 -0
  73. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/app_registry.py +0 -0
  74. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/backup.py +0 -0
  75. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/compute_remote.py +0 -0
  76. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/data_commit.py +0 -0
  77. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/data_explorer_table_prefs.py +0 -0
  78. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/database_explorer.py +0 -0
  79. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/db.py +0 -0
  80. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/device.py +0 -0
  81. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/engine_run.py +0 -0
  82. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/enrichment_lab.py +0 -0
  83. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/filter_lab.py +0 -0
  84. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/health.py +0 -0
  85. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/home_chat.py +0 -0
  86. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/ingestion_api.py +0 -0
  87. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/ingestion_compat.py +0 -0
  88. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/ingestion_sources.py +0 -0
  89. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/intelligence_lifecycle.py +0 -0
  90. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/llm.py +0 -0
  91. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/local_mcp.py +0 -0
  92. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/messenger_analytics.py +0 -0
  93. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/privacy_disclose.py +0 -0
  94. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/query_api.py +0 -0
  95. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/runtime_bootstrap.py +0 -0
  96. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/sanitization_ollama_config.py +0 -0
  97. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/signal.py +0 -0
  98. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/signal_extraction_config.py +0 -0
  99. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/source_scrub.py +0 -0
  100. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/sources.py +0 -0
  101. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/sources_overview.py +0 -0
  102. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/sync.py +0 -0
  103. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/uma_data.py +0 -0
  104. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/upgrades.py +0 -0
  105. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/usage.py +0 -0
  106. {topos_node-1.2.2 → topos_node-1.2.4}/topos/api/user_identity.py +0 -0
  107. {topos_node-1.2.2 → topos_node-1.2.4}/topos/auth.py +0 -0
  108. {topos_node-1.2.2 → topos_node-1.2.4}/topos/canonicalization/__init__.py +0 -0
  109. {topos_node-1.2.2 → topos_node-1.2.4}/topos/canonicalization/mappers/__init__.py +0 -0
  110. {topos_node-1.2.2 → topos_node-1.2.4}/topos/canonicalization/mappers/browser_activity_mapper.py +0 -0
  111. {topos_node-1.2.2 → topos_node-1.2.4}/topos/canonicalization/mappers/chatgpt_mapper.py +0 -0
  112. {topos_node-1.2.2 → topos_node-1.2.4}/topos/canonicalization/mappers/demo_mappers.py +0 -0
  113. {topos_node-1.2.2 → topos_node-1.2.4}/topos/canonicalization/mappers/grok_mapper.py +0 -0
  114. {topos_node-1.2.2 → topos_node-1.2.4}/topos/canonicalization/mappers/messenger_mapper.py +0 -0
  115. {topos_node-1.2.2 → topos_node-1.2.4}/topos/canonicalization/models.py +0 -0
  116. {topos_node-1.2.2 → topos_node-1.2.4}/topos/canonicalization/resolver.py +0 -0
  117. {topos_node-1.2.2 → topos_node-1.2.4}/topos/cli/__init__.py +0 -0
  118. {topos_node-1.2.2 → topos_node-1.2.4}/topos/cli/__main__.py +0 -0
  119. {topos_node-1.2.2 → topos_node-1.2.4}/topos/cli/commands.py +0 -0
  120. {topos_node-1.2.2 → topos_node-1.2.4}/topos/config/__init__.py +0 -0
  121. {topos_node-1.2.2 → topos_node-1.2.4}/topos/config/sanitization_ollama.py +0 -0
  122. {topos_node-1.2.2 → topos_node-1.2.4}/topos/config/signal_extraction.py +0 -0
  123. {topos_node-1.2.2 → topos_node-1.2.4}/topos/config/uvicorn_logging.json +0 -0
  124. {topos_node-1.2.2 → topos_node-1.2.4}/topos/contacts/__init__.py +0 -0
  125. {topos_node-1.2.2 → topos_node-1.2.4}/topos/contacts/identity.py +0 -0
  126. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/__init__.py +0 -0
  127. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/api_models.py +0 -0
  128. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/connection_resilience.py +0 -0
  129. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/device_helpers.py +0 -0
  130. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/errors.py +0 -0
  131. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/events.py +0 -0
  132. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/__init__.py +0 -0
  133. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/common.py +0 -0
  134. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/config.py +0 -0
  135. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/contacts_import.py +0 -0
  136. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/database_explorer.py +0 -0
  137. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/device.py +0 -0
  138. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/enrichment_lab.py +0 -0
  139. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/filter_lab.py +0 -0
  140. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/home_chat.py +0 -0
  141. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/messages.py +0 -0
  142. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/messenger_analytics.py +0 -0
  143. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/query.py +0 -0
  144. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/registry.py +0 -0
  145. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/routines.py +0 -0
  146. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/signal_features.py +0 -0
  147. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/sources.py +0 -0
  148. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/handlers/uma.py +0 -0
  149. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/logging.py +0 -0
  150. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/metrics.py +0 -0
  151. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/routine_access.py +0 -0
  152. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/startup_banner.py +0 -0
  153. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/table_layers.py +0 -0
  154. {topos_node-1.2.2 → topos_node-1.2.4}/topos/core/types.py +0 -0
  155. {topos_node-1.2.2 → topos_node-1.2.4}/topos/data_explorer_row_delete.py +0 -0
  156. {topos_node-1.2.2 → topos_node-1.2.4}/topos/data_explorer_table_prefs.py +0 -0
  157. {topos_node-1.2.2 → topos_node-1.2.4}/topos/data_explorer_tables.py +0 -0
  158. {topos_node-1.2.2 → topos_node-1.2.4}/topos/disclosure/__init__.py +0 -0
  159. {topos_node-1.2.2 → topos_node-1.2.4}/topos/disclosure/canonical_writer.py +0 -0
  160. {topos_node-1.2.2 → topos_node-1.2.4}/topos/disclosure/content_policy.py +0 -0
  161. {topos_node-1.2.2 → topos_node-1.2.4}/topos/disclosure/field_registry.py +0 -0
  162. {topos_node-1.2.2 → topos_node-1.2.4}/topos/disclosure/privacy_layer.py +0 -0
  163. {topos_node-1.2.2 → topos_node-1.2.4}/topos/disclosure/tier.py +0 -0
  164. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/__init__.py +0 -0
  165. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/backends/__init__.py +0 -0
  166. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/backends/base.py +0 -0
  167. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/backends/generative_prompts.py +0 -0
  168. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/backends/generative_response.py +0 -0
  169. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/backends/huggingface.py +0 -0
  170. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/backends/openai.py +0 -0
  171. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/backends/redpill.py +0 -0
  172. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/backends/stub.py +0 -0
  173. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/client.py +0 -0
  174. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/intake.py +0 -0
  175. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/memory_utils.py +0 -0
  176. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/model_cache.py +0 -0
  177. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/pipeline_memory.py +0 -0
  178. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/queue_manager.py +0 -0
  179. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/registration.py +0 -0
  180. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/result_formatter.py +0 -0
  181. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/router.py +0 -0
  182. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/scoped_token.py +0 -0
  183. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/transport.py +0 -0
  184. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/usage_guard.py +0 -0
  185. {topos_node-1.2.2 → topos_node-1.2.4}/topos/engine/validator.py +0 -0
  186. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/__init__.py +0 -0
  187. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/derived_tables.py +0 -0
  188. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/job_writer.py +0 -0
  189. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/__init__.py +0 -0
  190. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/base.py +0 -0
  191. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/__init__.py +0 -0
  192. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/_batch_limits.py +0 -0
  193. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/availability_scores_job.py +0 -0
  194. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/brief_fallback.py +0 -0
  195. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/dimension_summary_job.py +0 -0
  196. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/embeddings_job.py +0 -0
  197. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/emo_27_job.py +0 -0
  198. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/entities_job.py +0 -0
  199. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/fact_extraction_job.py +0 -0
  200. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/goal_extraction_job.py +0 -0
  201. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/pii_redaction_job.py +0 -0
  202. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/relationship_edges_job.py +0 -0
  203. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/sentiment_job.py +0 -0
  204. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/statistics_job.py +0 -0
  205. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/topic_clusters_job.py +0 -0
  206. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/topics_job.py +0 -0
  207. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/url_classification_core.py +0 -0
  208. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/url_classification_enrichment_job.py +0 -0
  209. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/canonical/url_classification_signal_job.py +0 -0
  210. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/raw/__init__.py +0 -0
  211. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/raw/attachments_job.py +0 -0
  212. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/raw/language_job.py +0 -0
  213. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/raw/time_normalization_job.py +0 -0
  214. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/jobs/raw/tool_calls_job.py +0 -0
  215. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/model_overrides.py +0 -0
  216. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/models/__init__.py +0 -0
  217. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/models/manager.py +0 -0
  218. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/models/mvp_defaults.py +0 -0
  219. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/models/registry.py +0 -0
  220. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/models/versioning.py +0 -0
  221. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/orchestrator.py +0 -0
  222. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/processor.py +0 -0
  223. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/progress_bar.py +0 -0
  224. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/registry_contract.py +0 -0
  225. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/signal_derivation_config.json +0 -0
  226. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/source_overrides.py +0 -0
  227. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment/website_classifier.py +0 -0
  228. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment_lab/__init__.py +0 -0
  229. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment_lab/bundles.py +0 -0
  230. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment_lab/model_resolve.py +0 -0
  231. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment_lab/schema.py +0 -0
  232. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment_lab/service.py +0 -0
  233. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment_lab/store.py +0 -0
  234. {topos_node-1.2.2 → topos_node-1.2.4}/topos/enrichment_lab/worker.py +0 -0
  235. {topos_node-1.2.2 → topos_node-1.2.4}/topos/evals/__init__.py +0 -0
  236. {topos_node-1.2.2 → topos_node-1.2.4}/topos/evals/enrichment_eval.py +0 -0
  237. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/__init__.py +0 -0
  238. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/consolidation.py +0 -0
  239. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/dossier.py +0 -0
  240. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/fact_materializer.py +0 -0
  241. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/graph_enrichers.py +0 -0
  242. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/graph_search.py +0 -0
  243. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/linking.py +0 -0
  244. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/maintenance.py +0 -0
  245. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/reads.py +0 -0
  246. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/entities/resolver.py +0 -0
  247. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/facts/__init__.py +0 -0
  248. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/facts/extract.py +0 -0
  249. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/facts/reads.py +0 -0
  250. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/facts/store.py +0 -0
  251. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/fit/evaluator.py +0 -0
  252. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/fit/opportunity_types.json +0 -0
  253. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/lifecycle/__init__.py +0 -0
  254. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/lifecycle/exclusions.py +0 -0
  255. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/lifecycle/gc.py +0 -0
  256. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/provenance/__init__.py +0 -0
  257. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/provenance/posture.py +0 -0
  258. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/provenance/roles.py +0 -0
  259. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/__init__.py +0 -0
  260. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/brief_canonical_loader.py +0 -0
  261. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/brief_ontology.py +0 -0
  262. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/brief_renderer.py +0 -0
  263. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/brief_schemas.py +0 -0
  264. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/chunking.py +0 -0
  265. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/data_health.py +0 -0
  266. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/definitions/brief_ontologies.json +0 -0
  267. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/definitions/intentions.json +0 -0
  268. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/definitions/profile.json +0 -0
  269. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/definitions/relationships.json +0 -0
  270. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/definitions/schema/dimension_definition.schema.json +0 -0
  271. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/definitions/time.json +0 -0
  272. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/definitions/work.json +0 -0
  273. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/dimension_briefs.py +0 -0
  274. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/dimension_definition_loader.py +0 -0
  275. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/dimension_profiles.py +0 -0
  276. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/dimension_registry.py +0 -0
  277. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/embed_context.py +0 -0
  278. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/extraction/artifact_router.py +0 -0
  279. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/extraction/artifact_store.py +0 -0
  280. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/extraction/rule_extractors.py +0 -0
  281. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/graph_sanitize.py +0 -0
  282. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/hybrid_search.py +0 -0
  283. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/incremental_clustering.py +0 -0
  284. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/model_recommendations.py +0 -0
  285. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/query_embed_cache.py +0 -0
  286. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/schemas.py +0 -0
  287. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/service.py +0 -0
  288. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/signal_object_store.py +0 -0
  289. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/source_hydration.py +0 -0
  290. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/structured_signal.py +0 -0
  291. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/topic_clustering.py +0 -0
  292. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/typed_stores/aggregates.py +0 -0
  293. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/typed_stores/scope_materializer.py +0 -0
  294. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/vector_codec.py +0 -0
  295. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/vector_math.py +0 -0
  296. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/signal/vector_settings.py +0 -0
  297. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/stats/__init__.py +0 -0
  298. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/stats/fold.py +0 -0
  299. {topos_node-1.2.2 → topos_node-1.2.4}/topos/features/stats/insights.py +0 -0
  300. {topos_node-1.2.2 → topos_node-1.2.4}/topos/filter_lab/__init__.py +0 -0
  301. {topos_node-1.2.2 → topos_node-1.2.4}/topos/filter_lab/bundles.py +0 -0
  302. {topos_node-1.2.2 → topos_node-1.2.4}/topos/filter_lab/schema.py +0 -0
  303. {topos_node-1.2.2 → topos_node-1.2.4}/topos/filter_lab/service.py +0 -0
  304. {topos_node-1.2.2 → topos_node-1.2.4}/topos/filter_lab/store.py +0 -0
  305. {topos_node-1.2.2 → topos_node-1.2.4}/topos/filter_lab/worker.py +0 -0
  306. {topos_node-1.2.2 → topos_node-1.2.4}/topos/home_chat/__init__.py +0 -0
  307. {topos_node-1.2.2 → topos_node-1.2.4}/topos/home_chat/schema.py +0 -0
  308. {topos_node-1.2.2 → topos_node-1.2.4}/topos/home_chat/store.py +0 -0
  309. {topos_node-1.2.2 → topos_node-1.2.4}/topos/hosted_pool_lease.py +0 -0
  310. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/__init__.py +0 -0
  311. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/checkpoints/__init__.py +0 -0
  312. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/checkpoints/checkpoint_store.py +0 -0
  313. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/checkpoints/sqlite_checkpoint_store.py +0 -0
  314. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/inbox_crypto.py +0 -0
  315. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/inbox_drain.py +0 -0
  316. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/jobs.py +0 -0
  317. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/journal_location_fanout.py +0 -0
  318. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/journal_time_log_normalize.py +0 -0
  319. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/log_preview.py +0 -0
  320. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/manager.py +0 -0
  321. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parser.py +0 -0
  322. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parsers/__init__.py +0 -0
  323. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parsers/base.py +0 -0
  324. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parsers/browser_parser.py +0 -0
  325. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parsers/calendar_parser.py +0 -0
  326. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parsers/chatgpt_conversation_flattener.py +0 -0
  327. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parsers/chatgpt_parser.py +0 -0
  328. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parsers/demo_file_parsers.py +0 -0
  329. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parsers/github_activity_parser.py +0 -0
  330. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parsers/grok_parser.py +0 -0
  331. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parsers/messenger_parser.py +0 -0
  332. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/parsers/voxterm_parser.py +0 -0
  333. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/progress.py +0 -0
  334. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/reprocess.py +0 -0
  335. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/sources/__init__.py +0 -0
  336. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/sources/base.py +0 -0
  337. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/sources/calendar.py +0 -0
  338. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/sources/chatgpt.py +0 -0
  339. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/sources/contact_importers.py +0 -0
  340. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/sources/grok.py +0 -0
  341. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/sources/imessage_reader.py +0 -0
  342. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/sources/signal_export_parser.py +0 -0
  343. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/sources/signal_reader.py +0 -0
  344. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/state_machine.py +0 -0
  345. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/triggers/__init__.py +0 -0
  346. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/triggers/file_trigger.py +0 -0
  347. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/triggers/sqlite_trigger.py +0 -0
  348. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/validation/__init__.py +0 -0
  349. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/validation/base.py +0 -0
  350. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/validation/schema_registry.py +0 -0
  351. {topos_node-1.2.2 → topos_node-1.2.4}/topos/ingestion/validation/schema_validator.py +0 -0
  352. {topos_node-1.2.2 → topos_node-1.2.4}/topos/lineage/__init__.py +0 -0
  353. {topos_node-1.2.2 → topos_node-1.2.4}/topos/lineage/provenance.py +0 -0
  354. {topos_node-1.2.2 → topos_node-1.2.4}/topos/lineage/tracker.py +0 -0
  355. {topos_node-1.2.2 → topos_node-1.2.4}/topos/mcp_stdio_proxy.py +0 -0
  356. {topos_node-1.2.2 → topos_node-1.2.4}/topos/observability/__init__.py +0 -0
  357. {topos_node-1.2.2 → topos_node-1.2.4}/topos/observability/alerts.py +0 -0
  358. {topos_node-1.2.2 → topos_node-1.2.4}/topos/observability/metrics.py +0 -0
  359. {topos_node-1.2.2 → topos_node-1.2.4}/topos/observability/tracing.py +0 -0
  360. {topos_node-1.2.2 → topos_node-1.2.4}/topos/openai_client.py +0 -0
  361. {topos_node-1.2.2 → topos_node-1.2.4}/topos/pipeline/__init__.py +0 -0
  362. {topos_node-1.2.2 → topos_node-1.2.4}/topos/pipeline/audit.py +0 -0
  363. {topos_node-1.2.2 → topos_node-1.2.4}/topos/pipeline/envelope.py +0 -0
  364. {topos_node-1.2.2 → topos_node-1.2.4}/topos/pipeline/stages.py +0 -0
  365. {topos_node-1.2.2 → topos_node-1.2.4}/topos/pipeline/stub_enqueue.py +0 -0
  366. {topos_node-1.2.2 → topos_node-1.2.4}/topos/projections/__init__.py +0 -0
  367. {topos_node-1.2.2 → topos_node-1.2.4}/topos/projections/vector_index/__init__.py +0 -0
  368. {topos_node-1.2.2 → topos_node-1.2.4}/topos/projections/vector_index/base.py +0 -0
  369. {topos_node-1.2.2 → topos_node-1.2.4}/topos/projections/vector_index/builders.py +0 -0
  370. {topos_node-1.2.2 → topos_node-1.2.4}/topos/projections/vector_index/health_checks.py +0 -0
  371. {topos_node-1.2.2 → topos_node-1.2.4}/topos/protocol/__init__.py +0 -0
  372. {topos_node-1.2.2 → topos_node-1.2.4}/topos/protocol/handled_message_types.json +0 -0
  373. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/__init__.py +0 -0
  374. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/audit.py +0 -0
  375. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/ddr.py +0 -0
  376. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/disclosure.py +0 -0
  377. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/fingerprint.py +0 -0
  378. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/game_layer.py +0 -0
  379. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/grant_cache.py +0 -0
  380. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/inference.py +0 -0
  381. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/intent.py +0 -0
  382. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/manifest.py +0 -0
  383. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/manifest_validation.py +0 -0
  384. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/minimizer.py +0 -0
  385. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/negotiation.py +0 -0
  386. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/pipeline.py +0 -0
  387. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/planner.py +0 -0
  388. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/retrieval.py +0 -0
  389. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/runtime.py +0 -0
  390. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/scope_registry_loader.py +0 -0
  391. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/session.py +0 -0
  392. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/session_utils.py +0 -0
  393. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/source_generation.py +0 -0
  394. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/turn_classifier.py +0 -0
  395. {topos_node-1.2.2 → topos_node-1.2.4}/topos/query/types.py +0 -0
  396. {topos_node-1.2.2 → topos_node-1.2.4}/topos/rate_limit.py +0 -0
  397. {topos_node-1.2.2 → topos_node-1.2.4}/topos/routines/__init__.py +0 -0
  398. {topos_node-1.2.2 → topos_node-1.2.4}/topos/routines/schema.py +0 -0
  399. {topos_node-1.2.2 → topos_node-1.2.4}/topos/routines/store.py +0 -0
  400. {topos_node-1.2.2 → topos_node-1.2.4}/topos/runtime_update.py +0 -0
  401. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sanitization/__init__.py +0 -0
  402. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sanitization/nsfw_classifier.py +0 -0
  403. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sanitization/ollama_transforms.py +0 -0
  404. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sanitization/privacy_filter.py +0 -0
  405. {topos_node-1.2.2 → topos_node-1.2.4}/topos/scope_resolution.py +0 -0
  406. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/__init__.py +0 -0
  407. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/container.py +0 -0
  408. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/embeddings/__init__.py +0 -0
  409. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/embeddings/base.py +0 -0
  410. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/embeddings/local.py +0 -0
  411. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/embeddings/remote.py +0 -0
  412. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/interfaces.py +0 -0
  413. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/llm/__init__.py +0 -0
  414. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/llm/base.py +0 -0
  415. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/llm/openai.py +0 -0
  416. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/local.py +0 -0
  417. {topos_node-1.2.2 → topos_node-1.2.4}/topos/services/postgres.py +0 -0
  418. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sources/__init__.py +0 -0
  419. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sources/bundled_canonical_triples.py +0 -0
  420. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sources/canonical_signal_defaults.py +0 -0
  421. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sources/runtime_install.py +0 -0
  422. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sources/scrub_service.py +0 -0
  423. {topos_node-1.2.2 → topos_node-1.2.4}/topos/startup_banner.py +0 -0
  424. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/__init__.py +0 -0
  425. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/adapters/__init__.py +0 -0
  426. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/adapters/factory.py +0 -0
  427. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/adapters/fakes.py +0 -0
  428. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/adapters/postgres/stores.py +0 -0
  429. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/adapters/protocols.py +0 -0
  430. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/adapters/sqlite/__init__.py +0 -0
  431. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/adapters/sqlite/stores.py +0 -0
  432. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/adapters/sqlite/vector_search.py +0 -0
  433. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/__init__.py +0 -0
  434. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/activity_tables.py +0 -0
  435. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/ai_chat/__init__.py +0 -0
  436. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/ai_chat/canonicalizer.py +0 -0
  437. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/ai_chat/mapper.py +0 -0
  438. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/ai_chat/model.py +0 -0
  439. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/ai_chat/tables.py +0 -0
  440. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/calendar_contacts_tables.py +0 -0
  441. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/canonical_store.py +0 -0
  442. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/mapping_store.py +0 -0
  443. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/canonical/postgres.py +0 -0
  444. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/__init__.py +0 -0
  445. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/client.py +0 -0
  446. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/connection_tuning.py +0 -0
  447. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/activity_events_content_v1.py +0 -0
  448. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/actor_role_v1.py +0 -0
  449. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/canonical_disclosure_v1.py +0 -0
  450. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/canonical_nsfw_v1.py +0 -0
  451. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/derivation_ledger_v1.py +0 -0
  452. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/entity_edges_validity_v1.py +0 -0
  453. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/episodes_v1.py +0 -0
  454. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/extraction_artifacts.py +0 -0
  455. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/journal_entries_duration_v1.py +0 -0
  456. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/journal_entries_ends_at_v1.py +0 -0
  457. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/journal_entries_people_v1.py +0 -0
  458. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/journal_entries_place_name_v1.py +0 -0
  459. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/journal_entries_starts_at_v1.py +0 -0
  460. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/remediation_person_model.py +0 -0
  461. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/scope_source_generation.py +0 -0
  462. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/signal_dimension_backfill_v1.py +0 -0
  463. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/signal_dimension_harness.py +0 -0
  464. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/signal_objects.py +0 -0
  465. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/signal_objects_period_v1.py +0 -0
  466. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/signal_objects_updated_by_v1.py +0 -0
  467. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/stage9_column_renames.py +0 -0
  468. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/vector_storage_v1.py +0 -0
  469. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/vector_storage_v2.py +0 -0
  470. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/vector_storage_v3.py +0 -0
  471. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/vector_storage_v4.py +0 -0
  472. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_clusters_v2.py +0 -0
  473. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_entities_v1.py +0 -0
  474. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_entity_review_v2.py +0 -0
  475. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_facts_v1.py +0 -0
  476. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_lifecycle_v1.py +0 -0
  477. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_mvp_phase0.py +0 -0
  478. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_mvp_phase1.py +0 -0
  479. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_mvp_phase4_messages_cutover.py +0 -0
  480. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_mvp_phase5_topic_clusters.py +0 -0
  481. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_mvp_phase6_dimension_briefs.py +0 -0
  482. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_mvp_query_quality.py +0 -0
  483. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_mvp_topic_clusters_coordination.py +0 -0
  484. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_stats_v1.py +0 -0
  485. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/migrations/wiki_timeline_v1.py +0 -0
  486. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/paths.py +0 -0
  487. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/postgres.py +0 -0
  488. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/schema.py +0 -0
  489. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/db/storage_breakdown.py +0 -0
  490. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/enrichment/__init__.py +0 -0
  491. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/enrichment/canonical_enrichment_store.py +0 -0
  492. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/enrichment/raw_enrichment_store.py +0 -0
  493. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/normalized/__init__.py +0 -0
  494. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/normalized/normalized_store.py +0 -0
  495. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/oplog/__init__.py +0 -0
  496. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/oplog/decision.py +0 -0
  497. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/oplog/oplog_store.py +0 -0
  498. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/oplog/postgres.py +0 -0
  499. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/person/identity_resolver.py +0 -0
  500. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/projections/__init__.py +0 -0
  501. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/projections/index_ops_store.py +0 -0
  502. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/projections/vector_index_store.py +0 -0
  503. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/raw/__init__.py +0 -0
  504. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/raw/browser_flat_tables.py +0 -0
  505. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/raw/file_store.py +0 -0
  506. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/raw/raw_store.py +0 -0
  507. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/raw/raw_tables_manager.py +0 -0
  508. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/raw/sqlite_raw_store.py +0 -0
  509. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/security/encryption.py +0 -0
  510. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/signal_identity.py +0 -0
  511. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/source_settings.py +0 -0
  512. {topos_node-1.2.2 → topos_node-1.2.4}/topos/storage/user_identity.py +0 -0
  513. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sync/__init__.py +0 -0
  514. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sync/client.py +0 -0
  515. {topos_node-1.2.2 → topos_node-1.2.4}/topos/sync_handlers.py +0 -0
  516. {topos_node-1.2.2 → topos_node-1.2.4}/topos/testing/__init__.py +0 -0
  517. {topos_node-1.2.2 → topos_node-1.2.4}/topos/testing/lifespan.py +0 -0
  518. {topos_node-1.2.2 → topos_node-1.2.4}/topos/uma_contact_enrichment.py +0 -0
  519. {topos_node-1.2.2 → topos_node-1.2.4}/topos/uma_filters.py +0 -0
  520. {topos_node-1.2.2 → topos_node-1.2.4}/topos/uma_resource_id.py +0 -0
  521. {topos_node-1.2.2 → topos_node-1.2.4}/topos/uma_rpt.py +0 -0
  522. {topos_node-1.2.2 → topos_node-1.2.4}/topos/upgrades/__init__.py +0 -0
  523. {topos_node-1.2.2 → topos_node-1.2.4}/topos/upgrades/manifests.json +0 -0
  524. {topos_node-1.2.2 → topos_node-1.2.4}/topos/upgrades/runner.py +0 -0
  525. {topos_node-1.2.2 → topos_node-1.2.4}/topos/utils/base_object.py +0 -0
  526. {topos_node-1.2.2 → topos_node-1.2.4}/topos/websocket_client.py +0 -0
  527. {topos_node-1.2.2 → topos_node-1.2.4}/topos_node.egg-info/dependency_links.txt +0 -0
  528. {topos_node-1.2.2 → topos_node-1.2.4}/topos_node.egg-info/entry_points.txt +0 -0
  529. {topos_node-1.2.2 → topos_node-1.2.4}/topos_node.egg-info/requires.txt +0 -0
  530. {topos_node-1.2.2 → topos_node-1.2.4}/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.2
3
+ Version: 1.2.4
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.2"
7
+ version = "1.2.4"
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.2"
4
- __version_info__ = (1, 2, 2)
3
+ __version__ = "1.2.4"
4
+ __version_info__ = (1, 2, 4)
5
5
 
6
6
  __all__ = ["__version__", "__version_info__"]
@@ -319,7 +319,14 @@ def _enrichment_coverage_core(source_id: str) -> Dict[str, Any]:
319
319
  "output_rows": {},
320
320
  }
321
321
  coverage_table = coverage_table_for_job(job_id)
322
- if coverage_table and _table_exists(conn, coverage_table):
322
+ if job_id == "timeline":
323
+ from ..features.timeline_projection import timeline_coverage_for_source
324
+
325
+ timeline_stats = timeline_coverage_for_source(conn, source_id)
326
+ job_info["enriched_records"] = timeline_stats["enriched_records"]
327
+ job_info["coverage_percent"] = timeline_stats["coverage_percent"]
328
+ job_info["output_rows"]["timeline"] = timeline_stats["enriched_records"]
329
+ elif coverage_table and _table_exists(conn, coverage_table):
323
330
  cols = _table_columns(conn, coverage_table)
324
331
  id_col = "message_id" if "message_id" in cols else "record_id"
325
332
  if "source_id" in cols and id_col in cols:
@@ -552,24 +559,50 @@ async def _generic_backfill_core(
552
559
  scanned = len(records)
553
560
 
554
561
  if only_missing:
555
- coverage_table = coverage_table_for_job(job_name)
556
- if coverage_table and _table_exists(db_conn, coverage_table):
557
- cols = _table_columns(db_conn, coverage_table)
558
- id_col = "message_id" if "message_id" in cols else "record_id"
559
- if "source_id" in cols and id_col in cols:
560
- done = {
561
- str(row[0])
562
- for row in db_conn.execute(
563
- f"SELECT DISTINCT {id_col} FROM {coverage_table} WHERE source_id=?",
564
- (source_id,),
565
- ).fetchall()
566
- if row[0]
562
+ if job_name == "timeline":
563
+ from ..features.timeline_projection import timeline_coverage_for_source
564
+
565
+ stats = timeline_coverage_for_source(db_conn, source_id)
566
+ if stats["missing_records"] == 0:
567
+ return {
568
+ "rows_scanned": scanned,
569
+ "rows_processed": 0,
570
+ "rows_skipped": scanned,
571
+ "rows_failed": 0,
572
+ "errors": [],
567
573
  }
568
- records = [r for r in records if _record_identifier(r) not in done]
574
+ else:
575
+ coverage_table = coverage_table_for_job(job_name)
576
+ if coverage_table and _table_exists(db_conn, coverage_table):
577
+ cols = _table_columns(db_conn, coverage_table)
578
+ id_col = "message_id" if "message_id" in cols else "record_id"
579
+ if "source_id" in cols and id_col in cols:
580
+ done = {
581
+ str(row[0])
582
+ for row in db_conn.execute(
583
+ f"SELECT DISTINCT {id_col} FROM {coverage_table} WHERE source_id=?",
584
+ (source_id,),
585
+ ).fetchall()
586
+ if row[0]
587
+ }
588
+ records = [r for r in records if _record_identifier(r) not in done]
569
589
 
570
590
  if isinstance(limit, int) and limit > 0:
571
591
  records = records[:limit]
572
592
 
593
+ if job_name == "timeline":
594
+ from ..features.timeline_projection import repair_timeline_for_source
595
+
596
+ report = repair_timeline_for_source(db_conn, source_id, missing_only=True, dry_run=False)
597
+ processed = int(report.get("totals", {}).get("written", 0))
598
+ return {
599
+ "rows_scanned": scanned,
600
+ "rows_processed": processed,
601
+ "rows_skipped": max(scanned - processed, 0),
602
+ "rows_failed": 0,
603
+ "errors": [],
604
+ }
605
+
573
606
  if not records:
574
607
  return {
575
608
  "rows_scanned": scanned,
@@ -950,6 +983,7 @@ async def _process_enrichment_core(
950
983
  job_names: Optional[List[str]] = None,
951
984
  force_reprocess: bool = False,
952
985
  include_signal: bool = True,
986
+ progress_updater: Optional[Any] = None,
953
987
  ) -> Dict[str, Any]:
954
988
  """Core logic for processing enrichment (reusable from HTTP and WebSocket).
955
989
 
@@ -1024,32 +1058,64 @@ async def _process_enrichment_core(
1024
1058
 
1025
1059
  # Define progress callback to update progress during execution
1026
1060
  progress_callback = None
1027
- try:
1028
- from ..enrichment.progress import get_progress
1029
- # Try to get progress object if it exists (created by handler)
1030
- progress_obj = get_progress(source_id) # Use source_id as fallback lookup
1031
- if progress_obj:
1032
- def progress_callback(
1033
- processed_count: int,
1034
- total_count: int,
1035
- job_name: str,
1036
- job_percent: float,
1037
- current_job_progress: float,
1038
- ):
1039
- """Update progress as jobs execute."""
1040
- estimated_messages_processed = int((job_percent / 100) * total_count)
1041
- jobs_complete = int((job_percent / 100) * len(jobs_to_run))
1042
- progress_obj.update(
1043
- messages_processed=estimated_messages_processed,
1044
- messages_skipped=0,
1045
- current_job_name=job_name,
1046
- current_job_progress_percent=current_job_progress,
1047
- jobs_complete=jobs_complete,
1048
- jobs_total=len(jobs_to_run),
1049
- )
1050
- except Exception:
1051
- pass # Progress callback is optional
1061
+ if progress_updater is not None:
1062
+ def progress_callback(
1063
+ processed_count: int,
1064
+ total_count: int,
1065
+ job_name: str,
1066
+ job_percent: float,
1067
+ current_job_progress: float,
1068
+ ):
1069
+ estimated_messages_processed = int((job_percent / 100) * total_count) if total_count else 0
1070
+ jobs_complete = int((job_percent / 100) * len(jobs_to_run)) if jobs_to_run else 0
1071
+ progress_updater(
1072
+ {
1073
+ "status": "processing",
1074
+ "progress_percent": min(100.0, job_percent),
1075
+ "messages_processed": estimated_messages_processed,
1076
+ "messages_skipped": 0,
1077
+ "messages_total": total_count,
1078
+ "current_job_name": job_name,
1079
+ "current_job_progress_percent": current_job_progress,
1080
+ "jobs_complete": jobs_complete,
1081
+ "jobs_total": len(jobs_to_run),
1082
+ "jobs_progress_percent": (jobs_complete / len(jobs_to_run) * 100) if jobs_to_run else 0.0,
1083
+ }
1084
+ )
1085
+ else:
1086
+ try:
1087
+ from ..enrichment.progress import get_progress
1088
+ progress_obj = get_progress(source_id)
1089
+ if progress_obj:
1090
+ def progress_callback(
1091
+ processed_count: int,
1092
+ total_count: int,
1093
+ job_name: str,
1094
+ job_percent: float,
1095
+ current_job_progress: float,
1096
+ ):
1097
+ estimated_messages_processed = int((job_percent / 100) * total_count)
1098
+ jobs_complete = int((job_percent / 100) * len(jobs_to_run))
1099
+ progress_obj.update(
1100
+ messages_processed=estimated_messages_processed,
1101
+ messages_skipped=0,
1102
+ current_job_name=job_name,
1103
+ current_job_progress_percent=current_job_progress,
1104
+ jobs_complete=jobs_complete,
1105
+ jobs_total=len(jobs_to_run),
1106
+ )
1107
+ except Exception:
1108
+ pass
1052
1109
 
1110
+ if progress_updater is not None:
1111
+ progress_updater(
1112
+ {
1113
+ "status": "processing",
1114
+ "messages_total": len(unprocessed_messages),
1115
+ "jobs_total": len(jobs_to_run),
1116
+ }
1117
+ )
1118
+
1053
1119
  enrichment_result = await orchestrator.run_canonical(
1054
1120
  unprocessed_messages,
1055
1121
  job_names=jobs_to_run,
@@ -11,6 +11,7 @@ from ..ingestion.ingest_helpers import ingest_file_payload, ingest_ui_payload
11
11
  from ..api.enrichment import _process_enrichment_core
12
12
  from ..engine.usage_observation import emit_usage_observation
13
13
  from ..sources import install_service
14
+ from ..sources.definitions import with_source_capabilities
14
15
 
15
16
  router = APIRouter()
16
17
  logger = logging.getLogger("topos.api.source_install")
@@ -140,7 +141,7 @@ async def _list_sources_core(payload: Dict[str, Any]) -> Dict[str, Any]:
140
141
  source_def = rec.source_definition_json if isinstance(rec.source_definition_json, dict) else {}
141
142
  if not sid or sid in active_by_source or not source_def:
142
143
  continue
143
- active_by_source[sid] = source_def
144
+ active_by_source[sid] = with_source_capabilities(source_def)
144
145
  sources = list(active_by_source.values())
145
146
  return {"status": "ok", "sources": sources}
146
147
 
@@ -43,6 +43,7 @@ def _normalize_ui_config(value: Any) -> dict[str, Any]:
43
43
  # Graph view prefs: the temporal window is personal (data volume/velocity
44
44
  # dependent — 2 days for some owners, 2 months for others). timeWindowDays
45
45
  # is a whole-day count; explicit null = all time; absent = unset.
46
+ # trackLookbackDays caps how far left the scrubber track extends.
46
47
  graph = value.get("graph")
47
48
  if isinstance(graph, dict) and "timeWindowDays" in graph:
48
49
  window = graph.get("timeWindowDays")
@@ -52,6 +53,14 @@ def _normalize_ui_config(value: Any) -> dict[str, Any]:
52
53
  days = int(window)
53
54
  if 1 <= days <= GRAPH_WINDOW_MAX_DAYS:
54
55
  out["graph"]["timeWindowDays"] = days
56
+ if isinstance(graph, dict) and "trackLookbackDays" in graph:
57
+ lookback = graph.get("trackLookbackDays")
58
+ if lookback is None:
59
+ out["graph"]["trackLookbackDays"] = None
60
+ elif isinstance(lookback, (int, float)) and not isinstance(lookback, bool):
61
+ days = int(lookback)
62
+ if 1 <= days <= GRAPH_WINDOW_MAX_DAYS:
63
+ out["graph"]["trackLookbackDays"] = days
55
64
  if isinstance(graph, dict):
56
65
  mode = graph.get("nodeColorMode")
57
66
  if mode in ("type", "community"):
@@ -172,6 +172,20 @@ async def startup_event() -> None:
172
172
  _start_upgrades(_upgrade_conn)
173
173
  except Exception as e:
174
174
  logger.warning("Upgrade runner at startup failed (non-fatal): %s", e)
175
+ try:
176
+ from .core.state import get_db_connection as _get_conn_for_pipeline
177
+ from .pipeline.job_runner import recover_pipeline_jobs, start_pipeline_worker
178
+ from .features.entities.graph_refresh import reconcile_graph_on_startup
179
+
180
+ _pipeline_conn = state.db_conn if state.db_conn is not None else _get_conn_for_pipeline()
181
+ if _pipeline_conn is not None:
182
+ recovered = recover_pipeline_jobs(_pipeline_conn)
183
+ if recovered:
184
+ logger.info("Recovered stale pipeline jobs on startup: count=%s", recovered)
185
+ reconcile_graph_on_startup(_pipeline_conn)
186
+ start_pipeline_worker(_get_conn_for_pipeline)
187
+ except Exception as e:
188
+ logger.warning("Pipeline worker at startup failed (non-fatal): %s", e)
175
189
  try:
176
190
  from .sources import install_service
177
191
 
@@ -0,0 +1,37 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Dict, List, Optional
5
+
6
+ from ...ingestion.parsers.base import NormalizedRecord
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class CanonicalRecord:
11
+ record_id: str
12
+ payload: Dict[str, str]
13
+ # Explicit canonical target table for dual-lane mappers. None means "the
14
+ # default table for the source's canonical_group_id" (existing behavior);
15
+ # single-lane mappers never need to set it.
16
+ table: Optional[str] = None
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class MappingMetadata:
21
+ source_id: str
22
+ mapping_version: str
23
+
24
+
25
+ class CanonicalMapper:
26
+ def map(self, normalized: NormalizedRecord) -> CanonicalRecord:
27
+ raise NotImplementedError
28
+
29
+ def map_many(self, normalized: NormalizedRecord) -> List[CanonicalRecord]:
30
+ """Multi-lane hook: one normalized record → one or more canonical rows,
31
+ each optionally targeting a different canonical table via
32
+ ``CanonicalRecord.table``. Default delegates to :meth:`map`, so
33
+ single-lane mappers keep working unchanged."""
34
+ return [self.map(normalized)]
35
+
36
+ def mapping_metadata(self, normalized: NormalizedRecord) -> MappingMetadata:
37
+ raise NotImplementedError
@@ -1,9 +1,16 @@
1
- """GitHub activity events → wiki activity_events canonical mapper."""
1
+ """GitHub activity events → dual-lane canonical mapper.
2
+
3
+ Every event maps to one ``activity_events`` row (single-lane behavior kept).
4
+ Additionally, each commit inside a PushEvent ``payload.commits[]`` maps to one
5
+ ``journal_entries`` row — a commit reads like a work-journal entry (category
6
+ 'code', content = commit message). Only PushEvent commits fan out; PRs/issues
7
+ stay activity-only.
8
+ """
2
9
 
3
10
  from __future__ import annotations
4
11
 
5
12
  from dataclasses import dataclass
6
- from typing import Any, Dict, Optional
13
+ from typing import Any, Dict, List, Optional
7
14
 
8
15
  from ...ingestion.parsers.base import NormalizedRecord
9
16
  from .base import CanonicalMapper, CanonicalRecord, MappingMetadata
@@ -82,9 +89,28 @@ def _title(repo_name: Optional[str], event_type: str, activity_type: str, event_
82
89
  return f"{prefix}: {activity_type.replace('_', ' ')}"
83
90
 
84
91
 
92
+ def _commit_timestamp(commit: Dict[str, Any]) -> Optional[str]:
93
+ """Commit timestamp if the payload carries one (REST events API usually
94
+ omits it — webhook-shaped payloads have ``timestamp``, some mirrors nest an
95
+ author date). None → caller falls back to the event created_at."""
96
+ for candidate in (commit.get("timestamp"), commit.get("date")):
97
+ if str(candidate or "").strip():
98
+ return str(candidate)
99
+ author = commit.get("author")
100
+ if isinstance(author, dict) and str(author.get("date") or "").strip():
101
+ return str(author["date"])
102
+ return None
103
+
104
+
105
+ def _branch_from_ref(ref: Optional[str]) -> Optional[str]:
106
+ if ref and ref.startswith("refs/heads/"):
107
+ return ref[len("refs/heads/") :]
108
+ return None
109
+
110
+
85
111
  @dataclass
86
112
  class GithubActivityCanonicalMapper(CanonicalMapper):
87
- version: str = "v1"
113
+ version: str = "v2"
88
114
 
89
115
  def map(self, normalized: NormalizedRecord) -> CanonicalRecord:
90
116
  payload = normalized.payload
@@ -108,5 +134,60 @@ class GithubActivityCanonicalMapper(CanonicalMapper):
108
134
  }
109
135
  return CanonicalRecord(record_id=canonical["event_id"], payload=canonical)
110
136
 
137
+ def map_many(self, normalized: NormalizedRecord) -> List[CanonicalRecord]:
138
+ """Dual lane: the activity_events row (always) + one journal_entries
139
+ row per PushEvent commit. Deterministic ids keep re-ingest idempotent:
140
+ entry_id 'github:{repo}:{sha}', source_record_id '{event_id}:{sha}'."""
141
+ records = [self.map(normalized)]
142
+ payload = normalized.payload
143
+ if str(payload.get("type") or "") != "PushEvent":
144
+ return records
145
+
146
+ event_record_id = str(payload.get("id") or normalized.record_id)
147
+ created_at = payload.get("created_at") or payload.get("occurred_at")
148
+ repo = payload.get("repo")
149
+ repo_name = str(repo.get("name")) if isinstance(repo, dict) and repo.get("name") else None
150
+ event_payload = payload.get("payload") if isinstance(payload.get("payload"), dict) else {}
151
+ ref = str(event_payload.get("ref") or "").strip() or None
152
+ branch = _branch_from_ref(ref)
153
+ commits = event_payload.get("commits")
154
+ if not isinstance(commits, list):
155
+ return records
156
+
157
+ for commit in commits:
158
+ if not isinstance(commit, dict):
159
+ continue
160
+ sha = str(commit.get("sha") or "").strip()
161
+ if not sha:
162
+ continue # no deterministic identity without a sha
163
+ entry_id = f"github:{repo_name}:{sha}" if repo_name else f"github:{sha}"
164
+ entry_at = _commit_timestamp(commit) or created_at
165
+ metadata: Dict[str, Any] = {
166
+ "repo": repo_name,
167
+ "sha": sha,
168
+ "ref": ref,
169
+ "branch": branch,
170
+ "event_id": f"github:{event_record_id}",
171
+ }
172
+ journal = {
173
+ "entry_id": entry_id,
174
+ "entry_at": entry_at,
175
+ # A commit is a point-in-time entry: no invented duration/start.
176
+ "starts_at": None,
177
+ "ends_at": entry_at,
178
+ "duration": None,
179
+ "mood_tag": None,
180
+ "category": "code",
181
+ "content": str(commit.get("message") or ""),
182
+ "people": None,
183
+ "place_name": None,
184
+ "source_record_id": f"{event_record_id}:{sha}",
185
+ "metadata_json": {k: v for k, v in metadata.items() if v is not None},
186
+ }
187
+ records.append(
188
+ CanonicalRecord(record_id=entry_id, payload=journal, table="journal_entries")
189
+ )
190
+ return records
191
+
111
192
  def mapping_metadata(self, normalized: NormalizedRecord) -> MappingMetadata:
112
193
  return MappingMetadata(source_id="github_activity", mapping_version=self.version)
@@ -30,7 +30,7 @@ class Settings(BaseSettings):
30
30
 
31
31
  environment: str = Field("development", env="ENVIRONMENT")
32
32
  log_format: Optional[str] = Field(None, env="LOG_FORMAT")
33
- log_level: str = Field("DEBUG", env="LOG_LEVEL")
33
+ log_level: str = Field("INFO", env="LOG_LEVEL")
34
34
 
35
35
  openai_base_url: str = Field("https://api.openai.com/v1", env="OPENAI_BASE_URL")
36
36
  openai_model: str = Field("gpt-4o-mini", env="OPENAI_MODEL")
@@ -48,7 +48,12 @@ class Settings(BaseSettings):
48
48
  "https://api.nodes.griptape.ai", env="GRIPTAPE_NODES_API_BASE_URL"
49
49
  )
50
50
 
51
- allowed_origins_raw: str = Field("http://localhost:3000", env="ALLOWED_ORIGINS")
51
+ allowed_origins_raw: str = Field(
52
+ "http://localhost:3000",
53
+ # pydantic v2 ignores Field(env=); without the alias this field only
54
+ # read ALLOWED_ORIGINS_RAW and the documented ALLOWED_ORIGINS was dead.
55
+ validation_alias=AliasChoices("ALLOWED_ORIGINS", "ALLOWED_ORIGINS_RAW"),
56
+ )
52
57
  allowed_origin_regex: Optional[str] = Field(None, env="ALLOWED_ORIGIN_REGEX")
53
58
  enable_health_auth: bool = Field(False, env="ENABLE_HEALTH_AUTH")
54
59
 
@@ -6,6 +6,7 @@ from collections import deque
6
6
  import json
7
7
  import logging
8
8
  import ssl
9
+ import threading
9
10
  from typing import Any, Awaitable, Callable, Dict
10
11
 
11
12
  import certifi
@@ -81,6 +82,9 @@ class ControlPlaneClient:
81
82
  self._presence_outbox_size = max(1, int(settings.control_plane_presence_outbox_size))
82
83
  self._presence_outbox: deque[dict[str, Any]] = deque(maxlen=self._presence_outbox_size)
83
84
  self._outbox_lock = asyncio.Lock()
85
+ # Thread-safe outbox for sync callers (Engine.run in worker threads via to_thread).
86
+ self._sync_outbox: deque[dict[str, Any]] = deque(maxlen=self._presence_outbox_size)
87
+ self._sync_outbox_lock = threading.Lock()
84
88
 
85
89
  self._backoff = ExponentialBackoff(
86
90
  ResilienceConfig(
@@ -290,6 +294,7 @@ class ControlPlaneClient:
290
294
  await self._handle_message(ws, data)
291
295
 
292
296
  async def _flush_presence_outbox(self) -> None:
297
+ await self._drain_sync_outbox_into_presence()
293
298
  async with self._outbox_lock:
294
299
  pending = list(self._presence_outbox)
295
300
  self._presence_outbox.clear()
@@ -301,6 +306,13 @@ class ControlPlaneClient:
301
306
  await self._enqueue_presence_message(message)
302
307
  break
303
308
 
309
+ async def _drain_sync_outbox_into_presence(self) -> None:
310
+ with self._sync_outbox_lock:
311
+ pending = list(self._sync_outbox)
312
+ self._sync_outbox.clear()
313
+ for message in pending:
314
+ await self._enqueue_presence_message(message)
315
+
304
316
  async def _enqueue_presence_message(self, message: Dict[str, Any]) -> None:
305
317
  async with self._outbox_lock:
306
318
  at_capacity = len(self._presence_outbox) >= self._presence_outbox_size
@@ -312,6 +324,36 @@ class ControlPlaneClient:
312
324
  )
313
325
  self._presence_outbox.append(dict(message))
314
326
 
327
+ def enqueue_unsolicited_message_threadsafe(self, message: Dict[str, Any]) -> None:
328
+ """Queue an unsolicited WS message from a sync/worker thread.
329
+
330
+ Enrichment runs Engine.run via asyncio.to_thread; those threads cannot safely
331
+ touch the websockets connection. Queue here and flush on the client's loop.
332
+ """
333
+ with self._sync_outbox_lock:
334
+ at_capacity = len(self._sync_outbox) >= self._presence_outbox_size
335
+ if at_capacity:
336
+ dropped = self._sync_outbox.popleft()
337
+ logger.warning(
338
+ "Sync outbox full; dropping oldest message type=%s",
339
+ dropped.get("type"),
340
+ )
341
+ self._sync_outbox.append(dict(message))
342
+
343
+ client_task = self._task
344
+ if client_task is None:
345
+ return
346
+ try:
347
+ loop = client_task.get_loop()
348
+ except Exception:
349
+ return
350
+ if not loop.is_running():
351
+ return
352
+ try:
353
+ asyncio.run_coroutine_threadsafe(self._flush_presence_outbox(), loop)
354
+ except Exception:
355
+ logger.debug("Failed to schedule sync outbox flush", exc_info=True)
356
+
315
357
  async def _send_ws_json(self, ws, payload: Dict[str, Any]) -> bool:
316
358
  if not ws:
317
359
  return False