topos-node 1.3.5__tar.gz → 1.3.6__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 (620) hide show
  1. {topos_node-1.3.5/topos_node.egg-info → topos_node-1.3.6}/PKG-INFO +1 -1
  2. {topos_node-1.3.5 → topos_node-1.3.6}/pyproject.toml +1 -1
  3. topos_node-1.3.6/tests/test_prewarm_first_run.py +61 -0
  4. topos_node-1.3.6/tests/test_system_computer_name.py +113 -0
  5. {topos_node-1.3.5 → topos_node-1.3.6}/topos/__version__.py +2 -2
  6. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/shell.py +7 -0
  7. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/upgrades.py +7 -1
  8. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/state.py +55 -1
  9. {topos_node-1.3.5 → topos_node-1.3.6}/topos/disclosure/privacy_layer.py +3 -2
  10. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/derivation_recovery.py +5 -2
  11. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/pii_redaction_job.py +3 -2
  12. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/topic_clusters_job.py +16 -0
  13. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/topic_clustering.py +2 -2
  14. {topos_node-1.3.5 → topos_node-1.3.6}/topos/pipeline/job_runner.py +80 -35
  15. {topos_node-1.3.5 → topos_node-1.3.6}/topos/pipeline/job_store.py +54 -2
  16. topos_node-1.3.6/topos/sanitization/prewarm.py +212 -0
  17. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/conversations_tables.py +3 -2
  18. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/write_gate.py +19 -0
  19. {topos_node-1.3.5 → topos_node-1.3.6}/topos/upgrades/manifests.json +6 -0
  20. {topos_node-1.3.5 → topos_node-1.3.6/topos_node.egg-info}/PKG-INFO +1 -1
  21. {topos_node-1.3.5 → topos_node-1.3.6}/topos_node.egg-info/SOURCES.txt +2 -0
  22. topos_node-1.3.5/topos/sanitization/prewarm.py +0 -32
  23. {topos_node-1.3.5 → topos_node-1.3.6}/LICENSE +0 -0
  24. {topos_node-1.3.5 → topos_node-1.3.6}/MANIFEST.in +0 -0
  25. {topos_node-1.3.5 → topos_node-1.3.6}/README.md +0 -0
  26. {topos_node-1.3.5 → topos_node-1.3.6}/setup.cfg +0 -0
  27. {topos_node-1.3.5 → topos_node-1.3.6}/shared/__init__.py +0 -0
  28. {topos_node-1.3.5 → topos_node-1.3.6}/shared/filtering.py +0 -0
  29. {topos_node-1.3.5 → topos_node-1.3.6}/shared/schema_registry.py +0 -0
  30. {topos_node-1.3.5 → topos_node-1.3.6}/tests/test_extensions.py +0 -0
  31. {topos_node-1.3.5 → topos_node-1.3.6}/tests/test_llm_integrations_storage.py +0 -0
  32. {topos_node-1.3.5 → topos_node-1.3.6}/tests/test_protocol_handled_types.py +0 -0
  33. {topos_node-1.3.5 → topos_node-1.3.6}/topos/__init__.py +0 -0
  34. {topos_node-1.3.5 → topos_node-1.3.6}/topos/analytics/__init__.py +0 -0
  35. {topos_node-1.3.5 → topos_node-1.3.6}/topos/analytics/duckdb_adapter.py +0 -0
  36. {topos_node-1.3.5 → topos_node-1.3.6}/topos/analytics/messenger_communities.py +0 -0
  37. {topos_node-1.3.5 → topos_node-1.3.6}/topos/analytics/messenger_graph.py +0 -0
  38. {topos_node-1.3.5 → topos_node-1.3.6}/topos/analytics/messenger_labels.py +0 -0
  39. {topos_node-1.3.5 → topos_node-1.3.6}/topos/analytics/profiles.py +0 -0
  40. {topos_node-1.3.5 → topos_node-1.3.6}/topos/analytics/query_engine.py +0 -0
  41. {topos_node-1.3.5 → topos_node-1.3.6}/topos/analytics/raw_queries.py +0 -0
  42. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/__init__.py +0 -0
  43. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/analytics.py +0 -0
  44. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/app_registry.py +0 -0
  45. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/backup.py +0 -0
  46. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/compute_remote.py +0 -0
  47. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/conversation_context.py +0 -0
  48. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/data_commit.py +0 -0
  49. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/data_explorer_table_prefs.py +0 -0
  50. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/database_explorer.py +0 -0
  51. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/db.py +0 -0
  52. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/device.py +0 -0
  53. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/engine_run.py +0 -0
  54. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/enrichment.py +0 -0
  55. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/enrichment_lab.py +0 -0
  56. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/facts_llm_config.py +0 -0
  57. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/filter_lab.py +0 -0
  58. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/health.py +0 -0
  59. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/home_chat.py +0 -0
  60. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/ingestion_api.py +0 -0
  61. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/ingestion_compat.py +0 -0
  62. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/ingestion_sources.py +0 -0
  63. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/intelligence_lifecycle.py +0 -0
  64. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/llm.py +0 -0
  65. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/local_mcp.py +0 -0
  66. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/messenger_analytics.py +0 -0
  67. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/privacy_disclose.py +0 -0
  68. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/query_api.py +0 -0
  69. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/runtime_bootstrap.py +0 -0
  70. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/sanitization_ollama_config.py +0 -0
  71. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/signal.py +0 -0
  72. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/signal_extraction_config.py +0 -0
  73. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/source_install.py +0 -0
  74. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/source_scrub.py +0 -0
  75. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/sources.py +0 -0
  76. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/sources_overview.py +0 -0
  77. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/sync.py +0 -0
  78. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/tool_index.py +0 -0
  79. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/ui_config.py +0 -0
  80. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/uma_data.py +0 -0
  81. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/usage.py +0 -0
  82. {topos_node-1.3.5 → topos_node-1.3.6}/topos/api/user_identity.py +0 -0
  83. {topos_node-1.3.5 → topos_node-1.3.6}/topos/app.py +0 -0
  84. {topos_node-1.3.5 → topos_node-1.3.6}/topos/auth.py +0 -0
  85. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/__init__.py +0 -0
  86. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/mappers/__init__.py +0 -0
  87. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/mappers/base.py +0 -0
  88. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/mappers/browser_activity_mapper.py +0 -0
  89. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/mappers/chatgpt_mapper.py +0 -0
  90. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/mappers/demo_mappers.py +0 -0
  91. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/mappers/documents_mapper.py +0 -0
  92. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/mappers/github_activity_mapper.py +0 -0
  93. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/mappers/google_calendar_mapper.py +0 -0
  94. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/mappers/grok_mapper.py +0 -0
  95. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/mappers/messenger_mapper.py +0 -0
  96. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/models.py +0 -0
  97. {topos_node-1.3.5 → topos_node-1.3.6}/topos/canonicalization/resolver.py +0 -0
  98. {topos_node-1.3.5 → topos_node-1.3.6}/topos/cli/__init__.py +0 -0
  99. {topos_node-1.3.5 → topos_node-1.3.6}/topos/cli/__main__.py +0 -0
  100. {topos_node-1.3.5 → topos_node-1.3.6}/topos/cli/assets/topos_blk_rounded.png +0 -0
  101. {topos_node-1.3.5 → topos_node-1.3.6}/topos/cli/assets/topos_white.png +0 -0
  102. {topos_node-1.3.5 → topos_node-1.3.6}/topos/cli/commands.py +0 -0
  103. {topos_node-1.3.5 → topos_node-1.3.6}/topos/cli/reprocess_cmd.py +0 -0
  104. {topos_node-1.3.5 → topos_node-1.3.6}/topos/cli/tray.py +0 -0
  105. {topos_node-1.3.5 → topos_node-1.3.6}/topos/config/__init__.py +0 -0
  106. {topos_node-1.3.5 → topos_node-1.3.6}/topos/config/conversation_context_llm.py +0 -0
  107. {topos_node-1.3.5 → topos_node-1.3.6}/topos/config/facts_llm.py +0 -0
  108. {topos_node-1.3.5 → topos_node-1.3.6}/topos/config/model_packs.py +0 -0
  109. {topos_node-1.3.5 → topos_node-1.3.6}/topos/config/sanitization_ollama.py +0 -0
  110. {topos_node-1.3.5 → topos_node-1.3.6}/topos/config/settings.py +0 -0
  111. {topos_node-1.3.5 → topos_node-1.3.6}/topos/config/signal_extraction.py +0 -0
  112. {topos_node-1.3.5 → topos_node-1.3.6}/topos/config/uvicorn_logging.json +0 -0
  113. {topos_node-1.3.5 → topos_node-1.3.6}/topos/contacts/__init__.py +0 -0
  114. {topos_node-1.3.5 → topos_node-1.3.6}/topos/contacts/identity.py +0 -0
  115. {topos_node-1.3.5 → topos_node-1.3.6}/topos/control_plane_client.py +0 -0
  116. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/__init__.py +0 -0
  117. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/api_models.py +0 -0
  118. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/connection_resilience.py +0 -0
  119. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/device_helpers.py +0 -0
  120. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/errors.py +0 -0
  121. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/events.py +0 -0
  122. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/__init__.py +0 -0
  123. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/common.py +0 -0
  124. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/complexity.py +0 -0
  125. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/config.py +0 -0
  126. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/contacts_import.py +0 -0
  127. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/database_explorer.py +0 -0
  128. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/device.py +0 -0
  129. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/enrichment.py +0 -0
  130. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/enrichment_lab.py +0 -0
  131. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/filter_lab.py +0 -0
  132. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/graph_query.py +0 -0
  133. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/home_chat.py +0 -0
  134. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/ingest.py +0 -0
  135. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/messages.py +0 -0
  136. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/messenger_analytics.py +0 -0
  137. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/query.py +0 -0
  138. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/registry.py +0 -0
  139. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/routines.py +0 -0
  140. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/signal_features.py +0 -0
  141. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/sources.py +0 -0
  142. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/timeline_daily.py +0 -0
  143. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/tool_index.py +0 -0
  144. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/handlers/uma.py +0 -0
  145. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/logging.py +0 -0
  146. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/metrics.py +0 -0
  147. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/routine_access.py +0 -0
  148. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/startup_banner.py +0 -0
  149. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/table_layers.py +0 -0
  150. {topos_node-1.3.5 → topos_node-1.3.6}/topos/core/types.py +0 -0
  151. {topos_node-1.3.5 → topos_node-1.3.6}/topos/data_explorer_row_delete.py +0 -0
  152. {topos_node-1.3.5 → topos_node-1.3.6}/topos/data_explorer_table_prefs.py +0 -0
  153. {topos_node-1.3.5 → topos_node-1.3.6}/topos/data_explorer_tables.py +0 -0
  154. {topos_node-1.3.5 → topos_node-1.3.6}/topos/disclosure/__init__.py +0 -0
  155. {topos_node-1.3.5 → topos_node-1.3.6}/topos/disclosure/canonical_writer.py +0 -0
  156. {topos_node-1.3.5 → topos_node-1.3.6}/topos/disclosure/content_policy.py +0 -0
  157. {topos_node-1.3.5 → topos_node-1.3.6}/topos/disclosure/field_registry.py +0 -0
  158. {topos_node-1.3.5 → topos_node-1.3.6}/topos/disclosure/tier.py +0 -0
  159. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/__init__.py +0 -0
  160. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/backends/__init__.py +0 -0
  161. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/backends/base.py +0 -0
  162. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/backends/generative_prompts.py +0 -0
  163. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/backends/generative_response.py +0 -0
  164. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/backends/huggingface.py +0 -0
  165. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/backends/ollama.py +0 -0
  166. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/backends/openai.py +0 -0
  167. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/backends/openai_compatible.py +0 -0
  168. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/backends/redpill.py +0 -0
  169. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/backends/stub.py +0 -0
  170. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/client.py +0 -0
  171. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/engine.py +0 -0
  172. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/intake.py +0 -0
  173. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/memory_utils.py +0 -0
  174. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/model_cache.py +0 -0
  175. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/pipeline_memory.py +0 -0
  176. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/queue_manager.py +0 -0
  177. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/registration.py +0 -0
  178. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/result_formatter.py +0 -0
  179. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/router.py +0 -0
  180. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/scoped_token.py +0 -0
  181. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/tasks.py +0 -0
  182. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/transport.py +0 -0
  183. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/usage_guard.py +0 -0
  184. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/usage_observation.py +0 -0
  185. {topos_node-1.3.5 → topos_node-1.3.6}/topos/engine/validator.py +0 -0
  186. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/__init__.py +0 -0
  187. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/catalog.py +0 -0
  188. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/derived_tables.py +0 -0
  189. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/job_writer.py +0 -0
  190. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/__init__.py +0 -0
  191. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/base.py +0 -0
  192. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/__init__.py +0 -0
  193. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/_batch_limits.py +0 -0
  194. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/_engine_runner.py +0 -0
  195. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/attention_triage_job.py +0 -0
  196. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/availability_scores_job.py +0 -0
  197. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/brief_fallback.py +0 -0
  198. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/complexity_snapshot_job.py +0 -0
  199. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/dimension_summary_job.py +0 -0
  200. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/embeddings_job.py +0 -0
  201. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/emo_27_job.py +0 -0
  202. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/entities_job.py +0 -0
  203. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/fact_extraction_job.py +0 -0
  204. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/goal_extraction_job.py +0 -0
  205. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/relationship_edges_job.py +0 -0
  206. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/sentiment_job.py +0 -0
  207. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/statistics_job.py +0 -0
  208. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/timeline_job.py +0 -0
  209. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/topics_job.py +0 -0
  210. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/url_classification_core.py +0 -0
  211. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/url_classification_enrichment_job.py +0 -0
  212. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/canonical/url_classification_signal_job.py +0 -0
  213. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/raw/__init__.py +0 -0
  214. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/raw/attachments_job.py +0 -0
  215. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/raw/language_job.py +0 -0
  216. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/raw/time_normalization_job.py +0 -0
  217. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/jobs/raw/tool_calls_job.py +0 -0
  218. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/model_overrides.py +0 -0
  219. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/models/__init__.py +0 -0
  220. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/models/manager.py +0 -0
  221. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/models/mvp_defaults.py +0 -0
  222. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/models/registry.py +0 -0
  223. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/models/versioning.py +0 -0
  224. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/orchestrator.py +0 -0
  225. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/pipeline_activity.py +0 -0
  226. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/processor.py +0 -0
  227. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/progress_bar.py +0 -0
  228. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/registry_contract.py +0 -0
  229. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/signal_derivation_config.json +0 -0
  230. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/source_overrides.py +0 -0
  231. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment/website_classifier.py +0 -0
  232. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment_lab/__init__.py +0 -0
  233. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment_lab/bundles.py +0 -0
  234. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment_lab/model_resolve.py +0 -0
  235. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment_lab/schema.py +0 -0
  236. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment_lab/service.py +0 -0
  237. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment_lab/store.py +0 -0
  238. {topos_node-1.3.5 → topos_node-1.3.6}/topos/enrichment_lab/worker.py +0 -0
  239. {topos_node-1.3.5 → topos_node-1.3.6}/topos/evals/__init__.py +0 -0
  240. {topos_node-1.3.5 → topos_node-1.3.6}/topos/evals/enrichment_eval.py +0 -0
  241. {topos_node-1.3.5 → topos_node-1.3.6}/topos/extensions.py +0 -0
  242. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/__init__.py +0 -0
  243. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/canonical.py +0 -0
  244. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/engine.py +0 -0
  245. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/entropy.py +0 -0
  246. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/focus.py +0 -0
  247. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/graph_metrics.py +0 -0
  248. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/influence.py +0 -0
  249. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/projection.py +0 -0
  250. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/snapshots.py +0 -0
  251. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/store.py +0 -0
  252. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/topic_merge.py +0 -0
  253. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/topics.py +0 -0
  254. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/topics_daily.py +0 -0
  255. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/complexity/volatility.py +0 -0
  256. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/__init__.py +0 -0
  257. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/affinity.py +0 -0
  258. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/affinity_owner.py +0 -0
  259. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/affinity_quality.py +0 -0
  260. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/consolidation.py +0 -0
  261. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/context_vectors.py +0 -0
  262. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/cypher.py +0 -0
  263. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/declared_mappings.py +0 -0
  264. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/dossier.py +0 -0
  265. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/edges.py +0 -0
  266. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/fact_materializer.py +0 -0
  267. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/graph_enrichers.py +0 -0
  268. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/graph_refresh.py +0 -0
  269. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/graph_search.py +0 -0
  270. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/linking.py +0 -0
  271. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/maintenance.py +0 -0
  272. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/reads.py +0 -0
  273. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/entities/resolver.py +0 -0
  274. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/facts/__init__.py +0 -0
  275. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/facts/extract.py +0 -0
  276. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/facts/llm_extract.py +0 -0
  277. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/facts/reads.py +0 -0
  278. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/facts/store.py +0 -0
  279. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/facts/verdicts.py +0 -0
  280. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/fit/evaluator.py +0 -0
  281. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/fit/opportunity_types.json +0 -0
  282. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/lifecycle/__init__.py +0 -0
  283. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/lifecycle/blackhole.py +0 -0
  284. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/lifecycle/blackhole_guard.py +0 -0
  285. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/lifecycle/blackhole_llm.py +0 -0
  286. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/lifecycle/blackhole_rebuild.py +0 -0
  287. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/lifecycle/derived_scrub.py +0 -0
  288. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/lifecycle/exclusions.py +0 -0
  289. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/lifecycle/gc.py +0 -0
  290. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/provenance/__init__.py +0 -0
  291. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/provenance/posture.py +0 -0
  292. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/provenance/roles.py +0 -0
  293. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/__init__.py +0 -0
  294. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/brief_canonical_loader.py +0 -0
  295. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/brief_ontology.py +0 -0
  296. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/brief_renderer.py +0 -0
  297. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/brief_schemas.py +0 -0
  298. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/chunking.py +0 -0
  299. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/cluster_labels.py +0 -0
  300. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/conversation_context.py +0 -0
  301. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/data_health.py +0 -0
  302. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/definitions/brief_ontologies.json +0 -0
  303. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/definitions/intentions.json +0 -0
  304. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/definitions/profile.json +0 -0
  305. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/definitions/relationships.json +0 -0
  306. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/definitions/schema/dimension_definition.schema.json +0 -0
  307. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/definitions/time.json +0 -0
  308. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/definitions/work.json +0 -0
  309. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/derived_spec.py +0 -0
  310. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/dimension_briefs.py +0 -0
  311. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/dimension_definition_loader.py +0 -0
  312. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/dimension_profiles.py +0 -0
  313. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/dimension_registry.py +0 -0
  314. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/embed_context.py +0 -0
  315. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/extraction/artifact_router.py +0 -0
  316. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/extraction/artifact_store.py +0 -0
  317. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/extraction/rule_extractors.py +0 -0
  318. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/graph_sanitize.py +0 -0
  319. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/hybrid_search.py +0 -0
  320. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/incremental_clustering.py +0 -0
  321. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/model_recommendations.py +0 -0
  322. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/query_embed_cache.py +0 -0
  323. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/schemas.py +0 -0
  324. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/service.py +0 -0
  325. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/signal_object_store.py +0 -0
  326. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/source_hydration.py +0 -0
  327. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/structured_signal.py +0 -0
  328. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/tool_index.py +0 -0
  329. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/typed_stores/aggregates.py +0 -0
  330. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/typed_stores/commitments.py +0 -0
  331. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/typed_stores/rhythm.py +0 -0
  332. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/typed_stores/scope_materializer.py +0 -0
  333. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/vector_codec.py +0 -0
  334. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/vector_math.py +0 -0
  335. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/signal/vector_settings.py +0 -0
  336. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/stats/__init__.py +0 -0
  337. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/stats/definitions.py +0 -0
  338. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/stats/engine.py +0 -0
  339. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/stats/fold.py +0 -0
  340. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/stats/insights.py +0 -0
  341. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/timeline_projection.py +0 -0
  342. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/timeline_rollup.py +0 -0
  343. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/triage/__init__.py +0 -0
  344. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/triage/badges.py +0 -0
  345. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/triage/daily.py +0 -0
  346. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/triage/dashboard.py +0 -0
  347. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/triage/instruments.py +0 -0
  348. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/triage/intents.py +0 -0
  349. {topos_node-1.3.5 → topos_node-1.3.6}/topos/features/triage/readiness.py +0 -0
  350. {topos_node-1.3.5 → topos_node-1.3.6}/topos/filter_lab/__init__.py +0 -0
  351. {topos_node-1.3.5 → topos_node-1.3.6}/topos/filter_lab/bundles.py +0 -0
  352. {topos_node-1.3.5 → topos_node-1.3.6}/topos/filter_lab/schema.py +0 -0
  353. {topos_node-1.3.5 → topos_node-1.3.6}/topos/filter_lab/service.py +0 -0
  354. {topos_node-1.3.5 → topos_node-1.3.6}/topos/filter_lab/store.py +0 -0
  355. {topos_node-1.3.5 → topos_node-1.3.6}/topos/filter_lab/worker.py +0 -0
  356. {topos_node-1.3.5 → topos_node-1.3.6}/topos/home_chat/__init__.py +0 -0
  357. {topos_node-1.3.5 → topos_node-1.3.6}/topos/home_chat/schema.py +0 -0
  358. {topos_node-1.3.5 → topos_node-1.3.6}/topos/home_chat/store.py +0 -0
  359. {topos_node-1.3.5 → topos_node-1.3.6}/topos/hosted_pool_lease.py +0 -0
  360. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/__init__.py +0 -0
  361. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/canonical_pipeline.py +0 -0
  362. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/checkpoints/__init__.py +0 -0
  363. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/checkpoints/checkpoint_store.py +0 -0
  364. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/checkpoints/sqlite_checkpoint_store.py +0 -0
  365. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/inbox_crypto.py +0 -0
  366. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/inbox_drain.py +0 -0
  367. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/ingest_helpers.py +0 -0
  368. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/jobs.py +0 -0
  369. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/journal_location_fanout.py +0 -0
  370. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/journal_time_log_normalize.py +0 -0
  371. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/local_sync.py +0 -0
  372. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/log_preview.py +0 -0
  373. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/manager.py +0 -0
  374. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parser.py +0 -0
  375. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/__init__.py +0 -0
  376. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/base.py +0 -0
  377. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/browser_parser.py +0 -0
  378. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/calendar_parser.py +0 -0
  379. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/chatgpt_conversation_flattener.py +0 -0
  380. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/chatgpt_parser.py +0 -0
  381. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/demo_file_parsers.py +0 -0
  382. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/documents_parser.py +0 -0
  383. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/github_activity_parser.py +0 -0
  384. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/google_calendar_parser.py +0 -0
  385. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/grok_parser.py +0 -0
  386. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/messenger_parser.py +0 -0
  387. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/parsers/voxterm_parser.py +0 -0
  388. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/progress.py +0 -0
  389. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/reprocess.py +0 -0
  390. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/sources/__init__.py +0 -0
  391. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/sources/base.py +0 -0
  392. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/sources/calendar.py +0 -0
  393. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/sources/chatgpt.py +0 -0
  394. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/sources/contact_importers.py +0 -0
  395. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/sources/grok.py +0 -0
  396. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/sources/imessage_reader.py +0 -0
  397. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/sources/signal_export_parser.py +0 -0
  398. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/sources/signal_reader.py +0 -0
  399. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/state_machine.py +0 -0
  400. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/triggers/__init__.py +0 -0
  401. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/triggers/file_trigger.py +0 -0
  402. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/triggers/sqlite_trigger.py +0 -0
  403. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/usage_inbox_dedupe.py +0 -0
  404. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/validation/__init__.py +0 -0
  405. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/validation/base.py +0 -0
  406. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/validation/schema_registry.py +0 -0
  407. {topos_node-1.3.5 → topos_node-1.3.6}/topos/ingestion/validation/schema_validator.py +0 -0
  408. {topos_node-1.3.5 → topos_node-1.3.6}/topos/lineage/__init__.py +0 -0
  409. {topos_node-1.3.5 → topos_node-1.3.6}/topos/lineage/provenance.py +0 -0
  410. {topos_node-1.3.5 → topos_node-1.3.6}/topos/lineage/tracker.py +0 -0
  411. {topos_node-1.3.5 → topos_node-1.3.6}/topos/llm_integrations_storage.py +0 -0
  412. {topos_node-1.3.5 → topos_node-1.3.6}/topos/mcp_stdio_proxy.py +0 -0
  413. {topos_node-1.3.5 → topos_node-1.3.6}/topos/observability/__init__.py +0 -0
  414. {topos_node-1.3.5 → topos_node-1.3.6}/topos/observability/alerts.py +0 -0
  415. {topos_node-1.3.5 → topos_node-1.3.6}/topos/observability/metrics.py +0 -0
  416. {topos_node-1.3.5 → topos_node-1.3.6}/topos/observability/tracing.py +0 -0
  417. {topos_node-1.3.5 → topos_node-1.3.6}/topos/openai_client.py +0 -0
  418. {topos_node-1.3.5 → topos_node-1.3.6}/topos/pipeline/__init__.py +0 -0
  419. {topos_node-1.3.5 → topos_node-1.3.6}/topos/pipeline/audit.py +0 -0
  420. {topos_node-1.3.5 → topos_node-1.3.6}/topos/pipeline/envelope.py +0 -0
  421. {topos_node-1.3.5 → topos_node-1.3.6}/topos/pipeline/stages.py +0 -0
  422. {topos_node-1.3.5 → topos_node-1.3.6}/topos/pipeline/stub_enqueue.py +0 -0
  423. {topos_node-1.3.5 → topos_node-1.3.6}/topos/projections/__init__.py +0 -0
  424. {topos_node-1.3.5 → topos_node-1.3.6}/topos/projections/vector_index/__init__.py +0 -0
  425. {topos_node-1.3.5 → topos_node-1.3.6}/topos/projections/vector_index/base.py +0 -0
  426. {topos_node-1.3.5 → topos_node-1.3.6}/topos/projections/vector_index/builders.py +0 -0
  427. {topos_node-1.3.5 → topos_node-1.3.6}/topos/projections/vector_index/health_checks.py +0 -0
  428. {topos_node-1.3.5 → topos_node-1.3.6}/topos/protocol/__init__.py +0 -0
  429. {topos_node-1.3.5 → topos_node-1.3.6}/topos/protocol/handled_message_types.json +0 -0
  430. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/__init__.py +0 -0
  431. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/audit.py +0 -0
  432. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/cohort_resolvers.py +0 -0
  433. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/ddr.py +0 -0
  434. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/disclosure.py +0 -0
  435. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/fingerprint.py +0 -0
  436. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/game_layer.py +0 -0
  437. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/grant_cache.py +0 -0
  438. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/inference.py +0 -0
  439. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/intent.py +0 -0
  440. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/manifest.py +0 -0
  441. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/manifest_validation.py +0 -0
  442. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/minimizer.py +0 -0
  443. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/negotiation.py +0 -0
  444. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/pipeline.py +0 -0
  445. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/planner.py +0 -0
  446. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/retrieval.py +0 -0
  447. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/runtime.py +0 -0
  448. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/scope_registry.json +0 -0
  449. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/scope_registry_loader.py +0 -0
  450. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/session.py +0 -0
  451. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/session_utils.py +0 -0
  452. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/source_generation.py +0 -0
  453. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/truth_facts.py +0 -0
  454. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/truth_prompts.py +0 -0
  455. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/turn_classifier.py +0 -0
  456. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/types.py +0 -0
  457. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/verify.py +0 -0
  458. {topos_node-1.3.5 → topos_node-1.3.6}/topos/query/verify_modes.py +0 -0
  459. {topos_node-1.3.5 → topos_node-1.3.6}/topos/rate_limit.py +0 -0
  460. {topos_node-1.3.5 → topos_node-1.3.6}/topos/routines/__init__.py +0 -0
  461. {topos_node-1.3.5 → topos_node-1.3.6}/topos/routines/schema.py +0 -0
  462. {topos_node-1.3.5 → topos_node-1.3.6}/topos/routines/store.py +0 -0
  463. {topos_node-1.3.5 → topos_node-1.3.6}/topos/runtime_housekeeping.py +0 -0
  464. {topos_node-1.3.5 → topos_node-1.3.6}/topos/runtime_shutdown.py +0 -0
  465. {topos_node-1.3.5 → topos_node-1.3.6}/topos/runtime_update.py +0 -0
  466. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sanitization/__init__.py +0 -0
  467. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sanitization/nsfw_classifier.py +0 -0
  468. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sanitization/ollama_transforms.py +0 -0
  469. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sanitization/privacy_filter.py +0 -0
  470. {topos_node-1.3.5 → topos_node-1.3.6}/topos/scope_resolution.py +0 -0
  471. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/__init__.py +0 -0
  472. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/container.py +0 -0
  473. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/embeddings/__init__.py +0 -0
  474. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/embeddings/base.py +0 -0
  475. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/embeddings/local.py +0 -0
  476. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/embeddings/remote.py +0 -0
  477. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/interfaces.py +0 -0
  478. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/llm/__init__.py +0 -0
  479. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/llm/base.py +0 -0
  480. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/llm/openai.py +0 -0
  481. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/local.py +0 -0
  482. {topos_node-1.3.5 → topos_node-1.3.6}/topos/services/postgres.py +0 -0
  483. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sources/__init__.py +0 -0
  484. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sources/bundled_canonical_triples.py +0 -0
  485. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sources/canonical_signal_defaults.py +0 -0
  486. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sources/definitions.py +0 -0
  487. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sources/install_service.py +0 -0
  488. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sources/registry.py +0 -0
  489. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sources/runtime_install.py +0 -0
  490. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sources/scrub_attribution.py +0 -0
  491. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sources/scrub_service.py +0 -0
  492. {topos_node-1.3.5 → topos_node-1.3.6}/topos/startup_banner.py +0 -0
  493. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/__init__.py +0 -0
  494. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/adapters/__init__.py +0 -0
  495. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/adapters/factory.py +0 -0
  496. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/adapters/fakes.py +0 -0
  497. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/adapters/postgres/stores.py +0 -0
  498. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/adapters/protocols.py +0 -0
  499. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/adapters/sqlite/__init__.py +0 -0
  500. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/adapters/sqlite/stores.py +0 -0
  501. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/adapters/sqlite/vector_search.py +0 -0
  502. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/__init__.py +0 -0
  503. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/activity_tables.py +0 -0
  504. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/ai_chat/__init__.py +0 -0
  505. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/ai_chat/canonicalizer.py +0 -0
  506. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/ai_chat/mapper.py +0 -0
  507. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/ai_chat/model.py +0 -0
  508. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/ai_chat/tables.py +0 -0
  509. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/calendar_contacts_tables.py +0 -0
  510. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/canonical_store.py +0 -0
  511. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/mapping_store.py +0 -0
  512. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/canonical/postgres.py +0 -0
  513. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/__init__.py +0 -0
  514. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/client.py +0 -0
  515. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/connection_tuning.py +0 -0
  516. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/__init__.py +0 -0
  517. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/activity_events_content_v1.py +0 -0
  518. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/actor_role_v1.py +0 -0
  519. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/affinity_pair_labels_v1.py +0 -0
  520. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/attention_triage_v1.py +0 -0
  521. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/attention_triage_v2.py +0 -0
  522. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/backup.py +0 -0
  523. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/calendar_events_scheduling_v1.py +0 -0
  524. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/canonical_address_book_v1.py +0 -0
  525. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/canonical_disclosure_v1.py +0 -0
  526. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/canonical_nsfw_v1.py +0 -0
  527. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/complexity_v1.py +0 -0
  528. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/conversation_context_v1.py +0 -0
  529. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/derivation_ledger_v1.py +0 -0
  530. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/documents_v1.py +0 -0
  531. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/enrichment_spec_version_v1.py +0 -0
  532. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/entity_affinity_v1.py +0 -0
  533. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/entity_blackhole_v1.py +0 -0
  534. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/entity_context_vectors_v1.py +0 -0
  535. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/entity_context_vectors_v2.py +0 -0
  536. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/entity_edges_validity_v1.py +0 -0
  537. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/entity_mentions_authored_v1.py +0 -0
  538. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/episodes_v1.py +0 -0
  539. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/extraction_artifacts.py +0 -0
  540. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/journal_entries_duration_v1.py +0 -0
  541. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/journal_entries_ends_at_v1.py +0 -0
  542. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/journal_entries_people_v1.py +0 -0
  543. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/journal_entries_place_name_v1.py +0 -0
  544. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/journal_entries_starts_at_v1.py +0 -0
  545. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/message_emotions_role_backfill_v1.py +0 -0
  546. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/message_emotions_role_v1.py +0 -0
  547. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/pipeline_jobs_v1.py +0 -0
  548. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/registry.py +0 -0
  549. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/registry_checksums.json +0 -0
  550. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/remediation_person_model.py +0 -0
  551. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/scope_source_generation.py +0 -0
  552. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/signal_dimension_backfill_v1.py +0 -0
  553. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/signal_dimension_harness.py +0 -0
  554. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/signal_objects.py +0 -0
  555. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/signal_objects_period_v1.py +0 -0
  556. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/signal_objects_updated_by_v1.py +0 -0
  557. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/signal_summaries_drop_v1.py +0 -0
  558. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/stage9_column_renames.py +0 -0
  559. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/vector_storage_v1.py +0 -0
  560. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/vector_storage_v2.py +0 -0
  561. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/vector_storage_v3.py +0 -0
  562. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/vector_storage_v4.py +0 -0
  563. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_clusters_v2.py +0 -0
  564. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_entities_v1.py +0 -0
  565. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_entity_review_v2.py +0 -0
  566. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_facts_v1.py +0 -0
  567. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_lifecycle_v1.py +0 -0
  568. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_mvp_phase0.py +0 -0
  569. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_mvp_phase1.py +0 -0
  570. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_mvp_phase4_messages_cutover.py +0 -0
  571. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_mvp_phase5_topic_clusters.py +0 -0
  572. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_mvp_phase6_dimension_briefs.py +0 -0
  573. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_mvp_query_quality.py +0 -0
  574. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_mvp_topic_clusters_coordination.py +0 -0
  575. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_stats_v1.py +0 -0
  576. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/migrations/wiki_timeline_v1.py +0 -0
  577. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/paths.py +0 -0
  578. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/postgres.py +0 -0
  579. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/schema.py +0 -0
  580. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/db/storage_breakdown.py +0 -0
  581. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/enrichment/__init__.py +0 -0
  582. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/enrichment/canonical_enrichment_store.py +0 -0
  583. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/enrichment/raw_enrichment_store.py +0 -0
  584. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/normalized/__init__.py +0 -0
  585. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/normalized/normalized_store.py +0 -0
  586. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/oplog/__init__.py +0 -0
  587. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/oplog/decision.py +0 -0
  588. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/oplog/oplog_store.py +0 -0
  589. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/oplog/postgres.py +0 -0
  590. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/person/identity_resolver.py +0 -0
  591. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/projections/__init__.py +0 -0
  592. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/projections/index_ops_store.py +0 -0
  593. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/projections/vector_index_store.py +0 -0
  594. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/raw/__init__.py +0 -0
  595. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/raw/browser_flat_tables.py +0 -0
  596. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/raw/file_store.py +0 -0
  597. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/raw/raw_store.py +0 -0
  598. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/raw/raw_tables_manager.py +0 -0
  599. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/raw/sqlite_raw_store.py +0 -0
  600. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/security/encryption.py +0 -0
  601. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/signal_identity.py +0 -0
  602. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/source_settings.py +0 -0
  603. {topos_node-1.3.5 → topos_node-1.3.6}/topos/storage/user_identity.py +0 -0
  604. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sync/__init__.py +0 -0
  605. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sync/client.py +0 -0
  606. {topos_node-1.3.5 → topos_node-1.3.6}/topos/sync_handlers.py +0 -0
  607. {topos_node-1.3.5 → topos_node-1.3.6}/topos/testing/__init__.py +0 -0
  608. {topos_node-1.3.5 → topos_node-1.3.6}/topos/testing/lifespan.py +0 -0
  609. {topos_node-1.3.5 → topos_node-1.3.6}/topos/uma_contact_enrichment.py +0 -0
  610. {topos_node-1.3.5 → topos_node-1.3.6}/topos/uma_filters.py +0 -0
  611. {topos_node-1.3.5 → topos_node-1.3.6}/topos/uma_resource_id.py +0 -0
  612. {topos_node-1.3.5 → topos_node-1.3.6}/topos/uma_rpt.py +0 -0
  613. {topos_node-1.3.5 → topos_node-1.3.6}/topos/upgrades/__init__.py +0 -0
  614. {topos_node-1.3.5 → topos_node-1.3.6}/topos/upgrades/runner.py +0 -0
  615. {topos_node-1.3.5 → topos_node-1.3.6}/topos/utils/base_object.py +0 -0
  616. {topos_node-1.3.5 → topos_node-1.3.6}/topos/websocket_client.py +0 -0
  617. {topos_node-1.3.5 → topos_node-1.3.6}/topos_node.egg-info/dependency_links.txt +0 -0
  618. {topos_node-1.3.5 → topos_node-1.3.6}/topos_node.egg-info/entry_points.txt +0 -0
  619. {topos_node-1.3.5 → topos_node-1.3.6}/topos_node.egg-info/requires.txt +0 -0
  620. {topos_node-1.3.5 → topos_node-1.3.6}/topos_node.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: topos-node
3
- Version: 1.3.5
3
+ Version: 1.3.6
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.3.5"
7
+ version = "1.3.6"
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"
@@ -0,0 +1,61 @@
1
+ """first_run decides whether the UI says "downloading" or "preparing".
2
+
3
+ Getting it wrong is a lie in the copy: a node that reports first_run=False shows
4
+ "preparing its language models… should only take a moment" while it fetches 2.9GB.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+
11
+ import pytest
12
+
13
+ from topos.sanitization import prewarm
14
+
15
+
16
+ @pytest.fixture
17
+ def cache_root(tmp_path, monkeypatch):
18
+ monkeypatch.setattr(prewarm, "_hf_cache_root", lambda: str(tmp_path))
19
+ return tmp_path
20
+
21
+
22
+ def _write_blobs(cache_root, repo_id: str, names: list[str]) -> None:
23
+ blobs = os.path.join(prewarm._repo_dir(str(cache_root), repo_id), "blobs")
24
+ os.makedirs(blobs, exist_ok=True)
25
+ for name in names:
26
+ open(os.path.join(blobs, name), "w").close()
27
+
28
+
29
+ REPO = prewarm.PRIVACY_FILTER_REPO
30
+
31
+
32
+ def test_absent_repo_is_not_present(cache_root):
33
+ assert prewarm._repo_present(REPO) is False
34
+
35
+
36
+ def test_started_but_empty_is_not_present(cache_root):
37
+ """The first instant of a first download — the directory exists, nothing is in it."""
38
+ _write_blobs(cache_root, REPO, [])
39
+ assert prewarm._repo_present(REPO) is False
40
+
41
+
42
+ def test_interrupted_download_is_not_present(cache_root):
43
+ """The case that made this a bug: a killed first run leaves .incomplete blobs
44
+ behind, and directory-existence alone would call that done."""
45
+ _write_blobs(cache_root, REPO, ["abc123.incomplete"])
46
+ assert prewarm._repo_present(REPO) is False
47
+
48
+
49
+ def test_partially_resumed_download_is_not_present(cache_root):
50
+ _write_blobs(cache_root, REPO, ["abc123", "def456.incomplete"])
51
+ assert prewarm._repo_present(REPO) is False
52
+
53
+
54
+ def test_complete_download_is_present(cache_root):
55
+ _write_blobs(cache_root, REPO, ["abc123", "def456"])
56
+ assert prewarm._repo_present(REPO) is True
57
+
58
+
59
+ def test_no_cache_root_is_not_present(monkeypatch):
60
+ monkeypatch.setattr(prewarm, "_hf_cache_root", lambda: None)
61
+ assert prewarm._repo_present(REPO) is False
@@ -0,0 +1,113 @@
1
+ """The machine name a Mac owner recognises, reported alongside the hostname.
2
+
3
+ `platform.node()` returns the network hostname, which on macOS is either a
4
+ mangled form of the computer name ("jonnys-macbook-pro.local") or something
5
+ meaningless from DHCP ("mac.lan" on the machine this was written on). The
6
+ frontend names a new self-hosted Topos after this, so a bad answer here shows up
7
+ as a Topos called "mac.lan".
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import subprocess
13
+
14
+ import pytest
15
+
16
+ from topos.core import state
17
+
18
+
19
+ @pytest.fixture(autouse=True)
20
+ def _clear_cache():
21
+ state._computer_name_cache = state._UNRESOLVED
22
+ yield
23
+ state._computer_name_cache = state._UNRESOLVED
24
+
25
+
26
+ def _fake_run(stdout: str):
27
+ def run(_cmd, **_kwargs):
28
+ return subprocess.CompletedProcess(args=_cmd, returncode=0, stdout=stdout, stderr="")
29
+
30
+ return run
31
+
32
+
33
+ def test_reports_the_computer_name_on_macos(monkeypatch):
34
+ monkeypatch.setattr(state.platform, "system", lambda: "Darwin")
35
+ monkeypatch.setattr(state.subprocess, "run", _fake_run("Jonny's MacBook Pro\n"))
36
+
37
+ assert state._macos_computer_name() == "Jonny's MacBook Pro"
38
+
39
+
40
+ def test_absent_off_macos(monkeypatch):
41
+ monkeypatch.setattr(state.platform, "system", lambda: "Linux")
42
+
43
+ def explode(*_a, **_k): # pragma: no cover - must never be reached
44
+ raise AssertionError("scutil is macOS-only and must not be spawned elsewhere")
45
+
46
+ monkeypatch.setattr(state.subprocess, "run", explode)
47
+ assert state._macos_computer_name() is None
48
+
49
+
50
+ @pytest.mark.parametrize("stdout", ["", " \n"])
51
+ def test_blank_output_is_no_name(monkeypatch, stdout):
52
+ monkeypatch.setattr(state.platform, "system", lambda: "Darwin")
53
+ monkeypatch.setattr(state.subprocess, "run", _fake_run(stdout))
54
+
55
+ assert state._macos_computer_name() is None
56
+
57
+
58
+ def test_failure_is_swallowed_and_never_retried(monkeypatch):
59
+ """A nicer label is never worth a startup failure — nor a stall on every request.
60
+
61
+ get_system_info runs inside async handlers, so an uncached spawn blocks the
62
+ event loop for the full timeout. On a Mac with no ComputerName that would be
63
+ a permanent per-request stall, so a miss is cached exactly like a hit and the
64
+ node falls back to the hostname for the rest of its life.
65
+ """
66
+ monkeypatch.setattr(state.platform, "system", lambda: "Darwin")
67
+ calls = {"n": 0}
68
+
69
+ def always_times_out(_cmd, **_kwargs):
70
+ calls["n"] += 1
71
+ raise subprocess.TimeoutExpired(cmd=_cmd, timeout=2.0)
72
+
73
+ monkeypatch.setattr(state.subprocess, "run", always_times_out)
74
+
75
+ assert state._macos_computer_name() is None
76
+ assert state._macos_computer_name() is None
77
+ assert state._macos_computer_name() is None
78
+ assert calls["n"] == 1
79
+
80
+
81
+ def test_non_macos_never_spawns_twice(monkeypatch):
82
+ monkeypatch.setattr(state.platform, "system", lambda: "Linux")
83
+ assert state._macos_computer_name() is None
84
+ # Cached, so a Linux node does not re-check platform on every request either.
85
+ assert state._computer_name_cache is None
86
+
87
+
88
+ def test_spawns_once_per_process(monkeypatch):
89
+ """get_device_info calls get_system_info twice, and the CP hits it on every connect."""
90
+ monkeypatch.setattr(state.platform, "system", lambda: "Darwin")
91
+ calls = {"n": 0}
92
+
93
+ def counting(_cmd, **_kwargs):
94
+ calls["n"] += 1
95
+ return subprocess.CompletedProcess(args=_cmd, returncode=0, stdout="q4\n", stderr="")
96
+
97
+ monkeypatch.setattr(state.subprocess, "run", counting)
98
+
99
+ for _ in range(5):
100
+ state._macos_computer_name()
101
+
102
+ assert calls["n"] == 1
103
+
104
+
105
+ def test_system_info_carries_computer_name_beside_hostname(monkeypatch):
106
+ """The dict is free-form and the control plane forwards it verbatim, so this
107
+ key is the entire wire contract — it lands as system_info.computer_name."""
108
+ monkeypatch.setattr(state, "_macos_computer_name", lambda: "Jonny's MacBook Pro")
109
+
110
+ info = state.get_system_info()
111
+
112
+ assert info["computer_name"] == "Jonny's MacBook Pro"
113
+ assert "hostname" in info
@@ -1,6 +1,6 @@
1
1
  """Topos Control Plane Version Information."""
2
2
 
3
- __version__ = "1.3.5"
4
- __version_info__ = (1, 3, 5)
3
+ __version__ = "1.3.6"
4
+ __version_info__ = (1, 3, 6)
5
5
 
6
6
  __all__ = ["__version__", "__version_info__"]
@@ -44,6 +44,8 @@ def _client_is_local(request: Request) -> bool:
44
44
 
45
45
  @router.get("/status")
46
46
  async def shell_status() -> dict:
47
+ from ..sanitization.prewarm import prewarm_status
48
+
47
49
  info = get_update_info()
48
50
  log_file = get_log_file_path()
49
51
  return {
@@ -59,6 +61,11 @@ async def shell_status() -> dict:
59
61
  "applying": _apply_state["applying"],
60
62
  "last_result": _apply_state["last_result"],
61
63
  },
64
+ # Additive: existing shells decode a fixed set of keys and ignore this.
65
+ # The tray cannot show it until the Swift ShellStatus struct adds the
66
+ # field (its CodingKeys are explicit), which needs its own signed build —
67
+ # shipping the data first means that release is a display change only.
68
+ "models": prewarm_status(),
62
69
  }
63
70
 
64
71
 
@@ -19,11 +19,17 @@ router = APIRouter()
19
19
  def _status_payload() -> Dict[str, Any]:
20
20
  from ..core.state import get_db_connection
21
21
  from ..features.entities.graph_refresh import status as refresh_status
22
+ from ..sanitization.prewarm import prewarm_status
22
23
  from ..upgrades.runner import runner_status
23
24
 
24
25
  conn = get_db_connection()
25
26
  upgrade = runner_status(conn) if conn is not None else {"enabled": False, "error": "no database"}
26
- return {"upgrade": upgrade, "graph_refresh": refresh_status()}
27
+ # `models` rides this existing read because the control plane already
28
+ # proxies it verbatim (engine_config.py returns `result.get("payload")`),
29
+ # `get_upgrade_status` is already allowlisted, and it is in the node's
30
+ # fast-inbound set — so it answers in milliseconds even while the prewarm
31
+ # thread is saturated downloading. No control-plane change, no CP deploy.
32
+ return {"upgrade": upgrade, "graph_refresh": refresh_status(), "models": prewarm_status()}
27
33
 
28
34
 
29
35
  @router.get("/v1/upgrade/status", dependencies=[Depends(require_api_key)])
@@ -5,6 +5,7 @@ import asyncio
5
5
  import os
6
6
  import platform
7
7
  import shutil
8
+ import subprocess
8
9
  import sqlite3
9
10
  import sys
10
11
  import threading
@@ -719,8 +720,10 @@ def _migrate_legacy_browser_plugin_app_ids(conn: sqlite3.Connection) -> int:
719
720
  f"UPDATE {UMA_ACCESS_REQUESTS_TABLE} SET app_id = ? WHERE app_id = ?",
720
721
  (BROWSER_HISTORY_PLUGIN_APP_ID, LEGACY_BROWSER_PLUGIN_APP_ID),
721
722
  )
723
+ # Commit even for 0 rows: the UPDATE opened an implicit transaction
724
+ # either way, and this runs at startup on the owner connection.
725
+ conn.commit()
722
726
  if cursor.rowcount:
723
- conn.commit()
724
727
  logger.info(
725
728
  "Migrated %s uma_access_requests rows: %s → %s",
726
729
  cursor.rowcount,
@@ -1082,6 +1085,53 @@ def get_total_memory_bytes() -> Optional[int]:
1082
1085
  return None
1083
1086
 
1084
1087
 
1088
+ # Resolved at most once per process.
1089
+ #
1090
+ # get_device_info calls get_system_info twice per request (services/local.py,
1091
+ # for engine_name and for the system dict), the control plane hits it on every
1092
+ # websocket connect, and these are async handlers — an uncached subprocess.run
1093
+ # blocks the event loop for its full timeout every time. Failures are cached
1094
+ # too, deliberately: a Mac with ComputerName unset, or a slow configd, would
1095
+ # otherwise stall EVERY request by up to the timeout, forever. One stall is a
1096
+ # blip; a permanent per-request stall is an outage. The cost of caching a miss
1097
+ # is a Topos named after the hostname instead, which is exactly the fallback
1098
+ # that already exists.
1099
+ _UNRESOLVED = object()
1100
+ _computer_name_cache: Any = _UNRESOLVED
1101
+
1102
+
1103
+ def _macos_computer_name() -> Optional[str]:
1104
+ """The name a Mac owner actually recognises — "Jonny's MacBook Pro".
1105
+
1106
+ `platform.node()` returns the network hostname, which on macOS is a mangled
1107
+ form of the same thing ("jonnys-macbook-pro.local") or, on many networks,
1108
+ something meaningless from DHCP. A Topos named after it reads like a server.
1109
+
1110
+ Read via scutil rather than a new dependency: pyobjc/SystemConfiguration
1111
+ would be a wheel added to every install for one string. Absolute path,
1112
+ bounded timeout, and every failure swallowed — a nicer label is never worth
1113
+ risking node startup. (Measured at 8ms, but a 1s bound proved tight enough to
1114
+ trip under load, and the first boot is the one call that has to answer.)
1115
+ """
1116
+ global _computer_name_cache
1117
+ if _computer_name_cache is not _UNRESOLVED:
1118
+ return _computer_name_cache
1119
+ if platform.system() != "Darwin":
1120
+ _computer_name_cache = None
1121
+ return None
1122
+ try:
1123
+ out = subprocess.run(
1124
+ ["/usr/sbin/scutil", "--get", "ComputerName"],
1125
+ capture_output=True,
1126
+ text=True,
1127
+ timeout=2.0,
1128
+ )
1129
+ _computer_name_cache = (out.stdout or "").strip() or None
1130
+ except Exception: # noqa: BLE001
1131
+ _computer_name_cache = None
1132
+ return _computer_name_cache
1133
+
1134
+
1085
1135
  def get_system_info() -> Dict[str, Any]:
1086
1136
  disk_total = None
1087
1137
  disk_free = None
@@ -1097,6 +1147,10 @@ def get_system_info() -> Dict[str, Any]:
1097
1147
  disk_free = None
1098
1148
  return {
1099
1149
  "hostname": platform.node() or None,
1150
+ # Free-form dict (api_models.py types `system` as Dict[str, Any]) and the
1151
+ # control plane forwards it verbatim, so this reaches fleet status as
1152
+ # system_info.computer_name with no CP change at all.
1153
+ "computer_name": _macos_computer_name(),
1100
1154
  "os": platform.system() or None,
1101
1155
  "os_version": platform.version() or None,
1102
1156
  "platform": platform.platform() or None,
@@ -349,8 +349,9 @@ async def run_privacy_disclosure_layer(
349
349
  ):
350
350
  nsfw_tagged += 1
351
351
 
352
- if updated or nsfw_tagged:
353
- conn.commit()
352
+ # Commit unconditionally: 0-row updates still opened an implicit
353
+ # transaction, which must not outlive this call.
354
+ conn.commit()
354
355
 
355
356
  duration_ms = int((time.perf_counter() - started) * 1000)
356
357
  logger.debug(
@@ -172,8 +172,11 @@ def clear_derivation_retry(
172
172
  (key,),
173
173
  )
174
174
  changed = int(cur.rowcount or 0)
175
- if changed:
176
- commit_connection(conn)
175
+ # Commit even when 0 rows matched: the UPDATE opened an implicit
176
+ # transaction regardless, and leaving it open makes the next
177
+ # BEGIN IMMEDIATE on this connection fail — which broke every
178
+ # topic_clusters batch (this runs after each successful job).
179
+ commit_connection(conn)
177
180
  if changed:
178
181
  logger.info(
179
182
  "[DERIVE:RETRY] cleared derivation debt job=%s batch=%s",
@@ -123,6 +123,7 @@ class PiiRedactionJob(BaseEnrichmentJob):
123
123
  if progress_callback:
124
124
  progress_callback(idx + 1, total)
125
125
 
126
- if updated:
127
- conn.commit()
126
+ # Commit unconditionally: a no-change pass may still have opened an
127
+ # implicit transaction, which must not outlive this job.
128
+ conn.commit()
128
129
  return [{"records_updated": updated}] if updated else []
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  import logging
4
5
  import os
5
6
  from typing import Any, Callable, Dict, List, Optional
@@ -138,6 +139,21 @@ class TopicClusterJob(BaseEnrichmentJob):
138
139
  self,
139
140
  canonical_messages: List[Dict[str, Any]],
140
141
  progress_callback: Optional[Callable[[int, int], None]] = None,
142
+ ) -> List[Dict[str, Any]]:
143
+ # The clustering path blocks for seconds at a time (k-means, the
144
+ # label-pool wait, persist under the write gate); on the event loop
145
+ # that starves every coroutine including the control-plane keepalive.
146
+ # get_db_connection is thread-local, so the worker thread gets its own
147
+ # handle — passing the loop thread's connection across would reinstate
148
+ # the cross-thread sharing behind the 2026-07-30 corruption.
149
+ return await asyncio.to_thread(
150
+ self._enrich_sync, canonical_messages, progress_callback
151
+ )
152
+
153
+ def _enrich_sync(
154
+ self,
155
+ canonical_messages: List[Dict[str, Any]],
156
+ progress_callback: Optional[Callable[[int, int], None]] = None,
141
157
  ) -> List[Dict[str, Any]]:
142
158
  conn = get_db_connection()
143
159
  if conn is None:
@@ -1250,11 +1250,11 @@ def recompute_topic_clusters(
1250
1250
  )
1251
1251
 
1252
1252
  _strip_member_vectors(clusters)
1253
- from ...storage.db.write_gate import sqlite_retry_busy, with_db_write
1253
+ from ...storage.db.write_gate import begin_immediate, sqlite_retry_busy, with_db_write
1254
1254
 
1255
1255
  with with_db_write():
1256
1256
  try:
1257
- sqlite_retry_busy(lambda: conn.execute("BEGIN IMMEDIATE"))
1257
+ begin_immediate(conn)
1258
1258
  _clear_all_topic_clusters(conn)
1259
1259
  persist_result = persist_topic_clusters(
1260
1260
  conn,
@@ -11,6 +11,7 @@ import uuid
11
11
  from typing import Any, Awaitable, Callable, Dict, Optional
12
12
 
13
13
  from .job_store import (
14
+ claim_matching_queued_jobs,
14
15
  claim_next_job,
15
16
  complete_job,
16
17
  fail_job,
@@ -144,17 +145,23 @@ async def _execute_topic_consolidation(payload: Dict[str, Any]) -> Dict[str, Any
144
145
  # what this executor exists to avoid.
145
146
  return {"status": "error", "error": "derivation in flight; will retry"}
146
147
 
147
- conn = get_db_connection()
148
- if conn is None:
149
- return {"status": "error", "error": "no database connection"}
148
+ def _run() -> Dict[str, Any]:
149
+ # Fetched INSIDE the worker thread: get_db_connection is thread-local,
150
+ # and handing the loop thread's connection across threads is the
151
+ # sharing that caused the 2026-07-30 transaction corruption.
152
+ own = get_db_connection()
153
+ if own is None:
154
+ return {"status": "error", "error": "no database connection"}
155
+ return recompute_topic_clusters(
156
+ own,
157
+ source_ids=list(_resolved_topic_cluster_source_ids()),
158
+ sync_batch_id=None,
159
+ min_records=3,
160
+ )
150
161
 
151
- result = await asyncio.to_thread(
152
- recompute_topic_clusters,
153
- conn,
154
- source_ids=list(_resolved_topic_cluster_source_ids()),
155
- sync_batch_id=None,
156
- min_records=3,
157
- )
162
+ result = await asyncio.to_thread(_run)
163
+ if str(result.get("status") or "") == "error":
164
+ return result
158
165
  logger.info("topic consolidation complete: %s", result)
159
166
  return {"status": "ok", "result": result}
160
167
 
@@ -167,13 +174,43 @@ EXECUTORS: Dict[str, ExecutorFn] = {
167
174
  }
168
175
 
169
176
 
177
+ #: Upper bound on inbox jobs merged into one derive batch. A CP backlog after
178
+ #: downtime arrives as one job per delivery (usually one record each); merging
179
+ #: them runs the per-batch jobs (dimension briefs, topic work) once instead of
180
+ #: once per delivery. Leftovers beyond the cap stay queued for the next cycle.
181
+ _COALESCE_MAX_JOBS = 100
182
+
183
+
184
+ def _coalesce_inbox_jobs(
185
+ conn, job: Dict[str, Any], payload: Dict[str, Any]
186
+ ) -> tuple[list[Dict[str, Any]], Dict[str, Any]]:
187
+ """Merge queued inbox jobs for the same source into this job's batch."""
188
+ siblings = claim_matching_queued_jobs(
189
+ conn,
190
+ lease_owner=_lease_owner,
191
+ kind=str(job["kind"]),
192
+ source_id=job.get("source_id"),
193
+ limit=_COALESCE_MAX_JOBS,
194
+ )
195
+ if not siblings:
196
+ return [job], payload
197
+ merged_records = list(payload.get("canonical_records") or [])
198
+ for sibling in siblings:
199
+ merged_records.extend((sibling.get("payload") or {}).get("canonical_records") or [])
200
+ merged_payload = {**payload, "canonical_records": merged_records}
201
+ logger.info(
202
+ "coalesced %d inbox jobs into one derive batch: source=%s records=%d",
203
+ len(siblings) + 1,
204
+ job.get("source_id"),
205
+ len(merged_records),
206
+ )
207
+ return [job, *siblings], merged_payload
208
+
209
+
170
210
  async def process_job(conn, job: Dict[str, Any]) -> None:
171
- """Run one claimed job to completion."""
211
+ """Run one claimed job (plus any coalesced siblings) to completion."""
172
212
  job_id = str(job["job_id"])
173
213
  kind = str(job["kind"])
174
- write_id = job.get("write_id")
175
- source_id = job.get("source_id")
176
- sync_batch_id = job.get("sync_batch_id")
177
214
 
178
215
  executor = EXECUTORS.get(kind)
179
216
  if executor is None:
@@ -184,38 +221,46 @@ async def process_job(conn, job: Dict[str, Any]) -> None:
184
221
  update_job_progress(conn, job_id, progress)
185
222
 
186
223
  payload = dict(job.get("payload") or {})
224
+ jobs = [job]
225
+ if kind == "inbox_deferred_enrichment":
226
+ jobs, payload = _coalesce_inbox_jobs(conn, job, payload)
187
227
  if kind == "enrichment_process_source":
188
228
  payload["_progress_updater"] = _progress_updater
189
229
 
190
230
  try:
191
231
  result = await executor(payload)
192
232
  if str(result.get("status") or "ok") == "error":
193
- fail_job(conn, job_id, error=str(result.get("error") or result.get("message") or "failed"))
194
- update_job_progress(conn, job_id, {"status": "failed", "result": result})
233
+ error = str(result.get("error") or result.get("message") or "failed")
234
+ for entry in jobs:
235
+ fail_job(conn, str(entry["job_id"]), error=error)
236
+ update_job_progress(conn, str(entry["job_id"]), {"status": "failed", "result": result})
195
237
  return
196
- complete_job(conn, job_id, detail=result)
197
- update_job_progress(
198
- conn,
199
- job_id,
200
- {
201
- "status": "completed",
202
- "messages_processed": result.get("messages_processed", 0),
203
- "records_created": result.get("records_created", {}),
204
- "errors": result.get("errors", []),
205
- },
206
- )
207
- if kind == "inbox_deferred_enrichment" and write_id:
208
- record_derivation_completion(
238
+ for entry in jobs:
239
+ entry_id = str(entry["job_id"])
240
+ complete_job(conn, entry_id, detail=result)
241
+ update_job_progress(
209
242
  conn,
210
- write_id=str(write_id),
211
- job_id=job_id,
212
- source_id=source_id,
213
- sync_batch_id=sync_batch_id,
243
+ entry_id,
244
+ {
245
+ "status": "completed",
246
+ "messages_processed": result.get("messages_processed", 0),
247
+ "records_created": result.get("records_created", {}),
248
+ "errors": result.get("errors", []),
249
+ },
214
250
  )
251
+ if kind == "inbox_deferred_enrichment" and entry.get("write_id"):
252
+ record_derivation_completion(
253
+ conn,
254
+ write_id=str(entry["write_id"]),
255
+ job_id=entry_id,
256
+ source_id=entry.get("source_id"),
257
+ sync_batch_id=entry.get("sync_batch_id"),
258
+ )
215
259
  except Exception as exc: # noqa: BLE001
216
260
  logger.warning("pipeline job failed job_id=%s kind=%s: %s", job_id, kind, exc, exc_info=exc)
217
- fail_job(conn, job_id, error=str(exc))
218
- update_job_progress(conn, job_id, {"status": "failed", "error": str(exc)})
261
+ for entry in jobs:
262
+ fail_job(conn, str(entry["job_id"]), error=str(exc))
263
+ update_job_progress(conn, str(entry["job_id"]), {"status": "failed", "error": str(exc)})
219
264
 
220
265
 
221
266
  #: Idle poll interval. Every tick claims against SQLite, so this is also how
@@ -8,7 +8,7 @@ import uuid
8
8
  from datetime import datetime, timedelta, timezone
9
9
  from typing import Any, Dict, List, Optional
10
10
 
11
- from ..storage.db.write_gate import commit_connection, sqlite_retry_busy, with_db_write
11
+ from ..storage.db.write_gate import begin_immediate, commit_connection, sqlite_retry_busy, with_db_write
12
12
 
13
13
  JOB_STATUSES = frozenset({"queued", "running", "done", "failed"})
14
14
  DEFAULT_LEASE_SECONDS = 300
@@ -145,7 +145,7 @@ def claim_next_job(
145
145
  expires = (datetime.now(timezone.utc) + timedelta(seconds=lease_seconds)).isoformat()
146
146
 
147
147
  with with_db_write():
148
- sqlite_retry_busy(lambda: conn.execute("BEGIN IMMEDIATE"))
148
+ begin_immediate(conn)
149
149
  try:
150
150
  query = "SELECT job_id FROM pipeline_jobs WHERE status='queued'"
151
151
  params: list[Any] = []
@@ -179,6 +179,58 @@ def claim_next_job(
179
179
  return get_job(conn, job_id)
180
180
 
181
181
 
182
+ def claim_matching_queued_jobs(
183
+ conn: sqlite3.Connection,
184
+ *,
185
+ lease_owner: str,
186
+ kind: str,
187
+ source_id: Optional[str],
188
+ lease_seconds: int = DEFAULT_LEASE_SECONDS,
189
+ limit: int = 100,
190
+ ) -> List[Dict[str, Any]]:
191
+ """Claim every queued job of ``kind`` for ``source_id``, oldest first.
192
+
193
+ Lets the worker coalesce a backlog — e.g. 40 one-record inbox deliveries —
194
+ into a single derive batch so the per-batch jobs (dimension briefs, topic
195
+ work) run once instead of once per delivery.
196
+ """
197
+ ensure_pipeline_jobs_schema(conn)
198
+ expires = (datetime.now(timezone.utc) + timedelta(seconds=lease_seconds)).isoformat()
199
+ claimed_ids: List[str] = []
200
+
201
+ with with_db_write():
202
+ begin_immediate(conn)
203
+ try:
204
+ query = "SELECT job_id FROM pipeline_jobs WHERE status='queued' AND kind=?"
205
+ params: list[Any] = [kind]
206
+ if source_id is None:
207
+ query += " AND source_id IS NULL"
208
+ else:
209
+ query += " AND source_id=?"
210
+ params.append(source_id)
211
+ query += " ORDER BY created_at ASC LIMIT ?"
212
+ params.append(int(limit))
213
+ rows = conn.execute(query, tuple(params)).fetchall()
214
+ for row in rows:
215
+ updated = conn.execute(
216
+ """
217
+ UPDATE pipeline_jobs
218
+ SET status='running', lease_owner=?, lease_expires_at=?,
219
+ started_at=COALESCE(started_at, datetime('now')),
220
+ updated_at=datetime('now')
221
+ WHERE job_id=? AND status='queued'
222
+ """,
223
+ (lease_owner, expires, str(row[0])),
224
+ )
225
+ if int(updated.rowcount or 0):
226
+ claimed_ids.append(str(row[0]))
227
+ sqlite_retry_busy(conn.commit)
228
+ except Exception:
229
+ conn.execute("ROLLBACK")
230
+ raise
231
+ return [job for job in (get_job(conn, jid) for jid in claimed_ids) if job is not None]
232
+
233
+
182
234
  def complete_job(
183
235
  conn: sqlite3.Connection,
184
236
  job_id: str,