virtual-context 0.2.8__tar.gz → 0.3.0__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 (226) hide show
  1. {virtual_context-0.2.8 → virtual_context-0.3.0}/PKG-INFO +140 -30
  2. {virtual_context-0.2.8 → virtual_context-0.3.0}/README.md +139 -29
  3. {virtual_context-0.2.8 → virtual_context-0.3.0}/pyproject.toml +1 -1
  4. virtual_context-0.3.0/tests/test_cli_init.py +218 -0
  5. virtual_context-0.3.0/tests/test_fact_redesign.py +95 -0
  6. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_format_agnostic.py +3 -3
  7. virtual_context-0.3.0/tests/test_store_recovery.py +192 -0
  8. virtual_context-0.3.0/virtual-context.yaml +109 -0
  9. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/__init__.py +1 -1
  10. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/cli/main.py +113 -5
  11. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/config.py +10 -1
  12. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/compaction_pipeline.py +8 -2
  13. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/compactor.py +12 -0
  14. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/composite_store.py +9 -0
  15. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/conversation_store.py +1 -0
  16. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/protocols.py +3 -0
  17. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/store.py +16 -0
  18. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/tag_generator.py +8 -0
  19. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/engine.py +1 -0
  20. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/dashboard.py +1 -1
  21. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/formats.py +445 -28
  22. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/handlers.py +22 -7
  23. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/message_filter.py +189 -26
  24. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/metrics.py +1 -1
  25. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/server.py +87 -43
  26. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/state.py +2 -2
  27. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/storage/falkordb.py +9 -0
  28. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/storage/filesystem.py +9 -0
  29. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/storage/neo4j.py +11 -0
  30. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/storage/postgres.py +32 -0
  31. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/storage/sqlite.py +83 -0
  32. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/types.py +2 -0
  33. virtual_context-0.2.8/tests/test_cli_init.py +0 -122
  34. {virtual_context-0.2.8 → virtual_context-0.3.0}/.gitignore +0 -0
  35. {virtual_context-0.2.8 → virtual_context-0.3.0}/LICENSE +0 -0
  36. {virtual_context-0.2.8 → virtual_context-0.3.0}/assets/dashboard.png +0 -0
  37. {virtual_context-0.2.8 → virtual_context-0.3.0}/assets/hero.png +0 -0
  38. {virtual_context-0.2.8 → virtual_context-0.3.0}/models.yaml +0 -0
  39. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/REGRESSION_MAP.md +0 -0
  40. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/conftest.py +0 -0
  41. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/docker-compose.test.yml +0 -0
  42. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/haiku/__init__.py +0 -0
  43. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/haiku/conftest.py +0 -0
  44. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/haiku/test_compaction.py +0 -0
  45. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/haiku/test_retrieval.py +0 -0
  46. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/haiku/test_tagging.py +0 -0
  47. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/ollama/__init__.py +0 -0
  48. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/ollama/conftest.py +0 -0
  49. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/ollama/test_compactor.py +0 -0
  50. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/ollama/test_pipeline.py +0 -0
  51. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/ollama/test_provider.py +0 -0
  52. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/ollama/test_tag_generator.py +0 -0
  53. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/proxy/__init__.py +0 -0
  54. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/proxy/test_dashboard_cors.py +0 -0
  55. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/proxy/test_metrics.py +0 -0
  56. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_assembler.py +0 -0
  57. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_backend_integration.py +0 -0
  58. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_budget_enforcement.py +0 -0
  59. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_compaction_commit_prune.py +0 -0
  60. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_compactor.py +0 -0
  61. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_compactor_concurrent.py +0 -0
  62. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_composite_store.py +0 -0
  63. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_config.py +0 -0
  64. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_context_bleed.py +0 -0
  65. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_conversation_identity.py +0 -0
  66. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_conversation_lifecycle.py +0 -0
  67. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_conversation_scoping.py +0 -0
  68. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_embedding_tag_generator.py +0 -0
  69. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_empty_turn_skip.py +0 -0
  70. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_engine_integration.py +0 -0
  71. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_engine_lookback.py +0 -0
  72. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_engine_state.py +0 -0
  73. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_fact_enrichment.py +0 -0
  74. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_fact_graph_integration.py +0 -0
  75. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_fact_link_checker.py +0 -0
  76. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_fact_link_query.py +0 -0
  77. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_fact_link_types.py +0 -0
  78. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_fact_links_sqlite.py +0 -0
  79. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_fill_pass.py +0 -0
  80. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_find_quote.py +0 -0
  81. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_headless.py +0 -0
  82. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_history_filter.py +0 -0
  83. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_idf_retrieval.py +0 -0
  84. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_ingest_index_integrity.py +0 -0
  85. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_longmemeval_auth.py +0 -0
  86. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_mcp_server.py +0 -0
  87. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_media.py +0 -0
  88. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_message_filter.py +0 -0
  89. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_metrics_persistence.py +0 -0
  90. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_model_catalog.py +0 -0
  91. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_model_limits.py +0 -0
  92. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_monitor.py +0 -0
  93. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_multi_instance.py +0 -0
  94. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_noop_fact_link_store.py +0 -0
  95. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_openrouter_provider.py +0 -0
  96. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_paging.py +0 -0
  97. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_passthrough_filter.py +0 -0
  98. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_presets.py +0 -0
  99. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_prev_context_leak.py +0 -0
  100. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_provider_adapters.py +0 -0
  101. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_proxy.py +0 -0
  102. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_proxy_dashboard.py +0 -0
  103. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_proxy_formats.py +0 -0
  104. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_proxy_message_filter.py +0 -0
  105. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_proxy_session.py +0 -0
  106. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_proxy_streaming.py +0 -0
  107. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_raw_content.py +0 -0
  108. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_recall_all.py +0 -0
  109. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_registry_lifecycle.py +0 -0
  110. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_request_captures_persistence.py +0 -0
  111. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_retriever.py +0 -0
  112. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_rrf_scoring.py +0 -0
  113. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_segmenter.py +0 -0
  114. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_semantic_search.py +0 -0
  115. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_sender_identity.py +0 -0
  116. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_session_cache.py +0 -0
  117. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_session_date.py +0 -0
  118. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_storage_protocols.py +0 -0
  119. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_store_sqlite.py +0 -0
  120. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_stub_turn_handling.py +0 -0
  121. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_supersession.py +0 -0
  122. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_supersession_migration.py +0 -0
  123. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_tag_canonicalizer.py +0 -0
  124. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_tag_consolidator.py +0 -0
  125. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_tag_generator.py +0 -0
  126. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_tag_splitter.py +0 -0
  127. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_telemetry.py +0 -0
  128. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_telemetry_integration.py +0 -0
  129. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_tool_loop.py +0 -0
  130. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_tool_output_interceptor.py +0 -0
  131. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_tool_result_filter.py +0 -0
  132. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_tool_tags.py +0 -0
  133. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_tui.py +0 -0
  134. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_turn_grouping.py +0 -0
  135. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_turn_tag_index.py +0 -0
  136. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_unified_budget.py +0 -0
  137. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_upstream_trim.py +0 -0
  138. {virtual_context-0.2.8 → virtual_context-0.3.0}/tests/test_verb_expansion.py +0 -0
  139. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual-context.yaml.example +0 -0
  140. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/cli/__init__.py +0 -0
  141. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/conversation_identity.py +0 -0
  142. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/__init__.py +0 -0
  143. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/assembler.py +0 -0
  144. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/embedding_provider.py +0 -0
  145. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/embedding_tag_generator.py +0 -0
  146. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/engine_utils.py +0 -0
  147. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/fact_query.py +0 -0
  148. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/fts_preprocessor.py +0 -0
  149. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/hint_builder.py +0 -0
  150. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/llm_utils.py +0 -0
  151. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/math_utils.py +0 -0
  152. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/model_catalog.py +0 -0
  153. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/monitor.py +0 -0
  154. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/paging_manager.py +0 -0
  155. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/provider_adapters.py +0 -0
  156. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/quote_search.py +0 -0
  157. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/retrieval_assembler.py +0 -0
  158. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/retrieval_scoring.py +0 -0
  159. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/retriever.py +0 -0
  160. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/search_engine.py +0 -0
  161. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/segmenter.py +0 -0
  162. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/semantic_search.py +0 -0
  163. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/tag_canonicalizer.py +0 -0
  164. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/tag_consolidator.py +0 -0
  165. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/tag_scoring.py +0 -0
  166. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/tag_splitter.py +0 -0
  167. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/tagging_pipeline.py +0 -0
  168. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/telemetry.py +0 -0
  169. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/temporal_resolver.py +0 -0
  170. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/tool_loop.py +0 -0
  171. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/tool_query.py +0 -0
  172. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/core/turn_tag_index.py +0 -0
  173. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/data/anthropic-tokenizer/tokenizer.json +0 -0
  174. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/ingest/__init__.py +0 -0
  175. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/ingest/curator.py +0 -0
  176. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/ingest/date_resolver.py +0 -0
  177. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/ingest/parsers.py +0 -0
  178. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/ingest/supersession.py +0 -0
  179. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/mcp/__init__.py +0 -0
  180. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/mcp/server.py +0 -0
  181. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/model_limits.py +0 -0
  182. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/openclaw/virtual-context.mjs +0 -0
  183. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/patterns.py +0 -0
  184. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/presets/__init__.py +0 -0
  185. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/presets/agentic.py +0 -0
  186. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/presets/base.py +0 -0
  187. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/presets/coding.py +0 -0
  188. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/providers/__init__.py +0 -0
  189. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/providers/anthropic.py +0 -0
  190. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/providers/base.py +0 -0
  191. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/providers/generic_openai.py +0 -0
  192. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/providers/ollama_native.py +0 -0
  193. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/__init__.py +0 -0
  194. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/_envelope.py +0 -0
  195. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/dashboard.html +0 -0
  196. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/helpers.py +0 -0
  197. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/media.py +0 -0
  198. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/multi.py +0 -0
  199. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/registry.py +0 -0
  200. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/session_cache.py +0 -0
  201. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/static/android-chrome-192x192.png +0 -0
  202. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/static/android-chrome-512x512.png +0 -0
  203. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/static/apple-touch-icon.png +0 -0
  204. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/static/favicon-16x16.png +0 -0
  205. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/static/favicon-32x32.png +0 -0
  206. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/static/favicon.ico +0 -0
  207. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/static/site.webmanifest +0 -0
  208. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/proxy/tool_output_interceptor.py +0 -0
  209. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/storage/__init__.py +0 -0
  210. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/storage/helpers.py +0 -0
  211. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/storage/noop_fact_link_store.py +0 -0
  212. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/token_counter.py +0 -0
  213. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/__init__.py +0 -0
  214. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/app.py +0 -0
  215. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/chat.tcss +0 -0
  216. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/chat_provider.py +0 -0
  217. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/headless.py +0 -0
  218. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/modals/__init__.py +0 -0
  219. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/modals/turn_inspector.py +0 -0
  220. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/state.py +0 -0
  221. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/widgets/__init__.py +0 -0
  222. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/widgets/budget_bar.py +0 -0
  223. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/widgets/chat_view.py +0 -0
  224. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/widgets/input_box.py +0 -0
  225. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/widgets/tag_panel.py +0 -0
  226. {virtual_context-0.2.8 → virtual_context-0.3.0}/virtual_context/tui/widgets/turn_list.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: virtual-context
3
- Version: 0.2.8
3
+ Version: 0.3.0
4
4
  Summary: OS-style virtual memory for LLM session context management
5
5
  Project-URL: Homepage, https://virtual-context.com
6
6
  Project-URL: Repository, https://github.com/virtual-context/virtual-context
@@ -180,10 +180,16 @@ proxy:
180
180
  **Daemon mode:** run as a background service:
181
181
 
182
182
  ```bash
183
- virtual-context onboard --install-daemon --upstream https://api.anthropic.com
183
+ # Creates ~/.virtualcontext/ with config + data, installs + starts daemon
184
+ virtual-context daemon install --upstream https://api.anthropic.com
185
+
186
+ # Or: guided interactive setup with daemon
187
+ virtual-context onboard --wizard --install-daemon
184
188
  ```
185
189
 
186
- Daemon setup docs (macOS `launchd`, Linux `systemd --user`, Windows Task Scheduler): [`docs/install.md`](docs/install.md)
190
+ Daemon lifecycle: `daemon status | start | stop | restart | uninstall`
191
+
192
+ Full setup docs (macOS `launchd`, Linux `systemd --user`, Windows Task Scheduler): [`docs/install.md`](docs/install.md)
187
193
 
188
194
  ### Python SDK
189
195
 
@@ -276,6 +282,7 @@ Exposes virtual-context as an MCP server for integration with Claude Desktop, Cu
276
282
  | Tool | `collapse_topic` | Collapse a topic back to summary or none |
277
283
  | Tool | `find_quote` | Full-text search across all stored conversation text |
278
284
  | Tool | `query_facts` | Structured fact lookup with subject/verb/object/status filters |
285
+ | Tool | `restore_tool` | Recover full content from compacted chain stubs or compressed media |
279
286
  | Resource | `virtualcontext://domains` | List all tags |
280
287
  | Resource | `virtualcontext://domains/{tag}` | Summaries for a specific tag |
281
288
  | Prompt | `recall` | Suggest context retrieval for a topic |
@@ -287,11 +294,12 @@ Exposes virtual-context as an MCP server for integration with Claude Desktop, Cu
287
294
  User message arrives
288
295
  │
289
296
  ▼
290
- Session routing (proxy mode)
291
- │ ├─ Extract session ID from <!-- vc:session=UUID --> markers in assistant messages
292
- │ ├─ Route to existing session or load persisted state from store
293
- │ ├─ No marker? → reuse default session (first request) or create new
294
- │ └─ Strip session markers before forwarding to upstream
297
+ Conversation routing (proxy mode)
298
+ │ ├─ Extract conversation ID from <!-- vc:conversation=UUID --> markers
299
+ │ ├─ Route to existing conversation or load persisted state from store + Redis
300
+ │ ├─ No marker? → derive stable ID from system prompt hash + format
301
+ │ ├─ Redis session cache: lossless restart, write-through history persistence
302
+ │ └─ Strip conversation markers before forwarding upstream
295
303
  │
296
304
  ▼
297
305
  Strip client envelope + extract metadata
@@ -301,13 +309,20 @@ Strip client envelope + extract metadata
301
309
  │ └─ Metadata preserved on Message.metadata for downstream use
302
310
  │
303
311
  ▼
312
+ Media compression (all paths — passthrough and active)
313
+ │ ├─ Detect base64 images across all 4 formats (Anthropic, OpenAI Chat, Responses, Gemini)
314
+ │ ├─ Compress to JPEG, store originals to disk for later recovery
315
+ │ ├─ Image token counting uses Anthropic formula: (width × height) / 750
316
+ │ └─ A 391KB screenshot → ~40KB compressed, saving ~88k tokens
317
+ │
318
+ ▼
304
319
  History ingestion (first request only)
305
320
  │ ├─ Extract and tag all prior user+assistant pairs → bootstrap TurnTagIndex
306
321
  │ ├─ Stub detection: media attachments/image placeholders get _stub tag (skip LLM tagger)
307
322
  │ └─ Conversation-scoped: each conversation's index is independent
308
323
  │
309
324
  ▼
310
- Inbound tagging - identify what this message is about
325
+ Inbound tagging — identify what this message is about
311
326
  │ ├─ Embedding tagger (recommended): cosine similarity against existing tag vocabulary
312
327
  │ │ (closed-set, deterministic, can't hallucinate novel tags)
313
328
  │ ├─ LLM / keyword tagger: alternative with vocabulary feedback
@@ -329,39 +344,66 @@ Assemble context within token budget
329
344
  │ └─ Tag sections: retrieved summaries ordered by tag priority
330
345
  │
331
346
  ▼
347
+ Chain collapse — compress tool-bearing history turns
348
+ │ ├─ Group raw messages into logical turns via group_into_turns()
349
+ │ ├─ Tool chains (assistant tool_use → user tool_result → assistant) → compact stubs
350
+ │ ├─ Stubs contain tool names, truncated previews, and restore refs
351
+ │ ├─ Handles all 4 formats: Anthropic, OpenAI Chat, OpenAI Responses, Gemini
352
+ │ ├─ Deep compaction: drops stubs entirely past configurable age threshold
353
+ │ └─ Non-tool turns outside protected window are dropped (summaries cover them)
354
+ │
355
+ ▼
356
+ Store-backed recovery (when client truncates history)
357
+ │ ├─ Detect truncation: payload turns < 70% of stored turns
358
+ �� ├─ Recover chain snapshots from durable store (compact stubs with metadata)
359
+ │ ├─ Recover recent turns from stored turn_messages
360
+ │ └─ Sanitize restored turns: strip thinking blocks, replace media with placeholders
361
+ │
362
+ ▼
332
363
  Filter conversation history
333
364
  │ ├─ Drop turns whose tags don't overlap with inbound tags
334
365
  │ ├─ Preserve tool chains atomically (tool_use ↔ tool_result never separated)
366
+ │ ├─ Protected zone intrusion: stub tool results in protected turns 3+ when zone exceeds budget %
335
367
  │ ├─ Protect recent turns (always kept regardless of tags)
336
368
  │ └─ Temporal queries skip filtering entirely
337
369
  │
338
370
  ▼
339
- Inject <virtual-context> block → forward enriched request to LLM
371
+ Budget enforcement — iterative payload reduction
372
+ │ ├─ Scan reducible items: conversation text, tool results, thinking blocks, images
373
+ │ ├─ Cut largest reducible item per iteration until under budget
374
+ │ ├─ Bloat fallback: if VC enrichment exceeds inbound size, fall back to pure passthrough
375
+ │ └─ Upstream trim: final trim to model's actual context window limit
376
+ │
377
+ ▼
378
+ Fill pass — replenish context after compression
379
+ │ ├─ Phase 1a: overflow tag summaries (topics that didn't fit during assembly)
380
+ │ ├─ Phase 1b: breadth summaries (sample from remaining tags)
381
+ │ ├─ Phase 2: recent turns from store (newest first)
382
+ │ └─ Target: soft threshold between floor and budget ceiling
383
+ │
384
+ ▼
385
+ Inject <virtual-context> block into last user message → forward to LLM
386
+ │ (injected into user messages, not system prompt, for Anthropic cache stability)
340
387
  │
341
388
  ▼
342
389
  LLM processes enriched context → produces response
343
390
  │
344
391
  ▼
345
- Inject session marker into response (proxy mode)
346
- │ ├─ Streaming: emit final SSE delta with <!-- vc:session=UUID -->
347
- │ └─ Non-streaming: append marker to last text content block
392
+ Inject conversation marker into response (proxy mode)
393
+ │ ├─ Streaming: emit final SSE delta with <!-- vc:conversation=UUID -->
394
+ │ ├─ Non-streaming: append marker to last text content block
395
+ │ └─ Skip injection when payload already contains a conversation marker
348
396
  │
349
397
  ▼
350
- Response tagging - LLM tags the full user+assistant pair (background thread)
398
+ Response tagging — LLM tags the full user+assistant pair (background thread)
351
399
  │ ├─ Context lookback: feed N recent pairs as tagger context for short/ambiguous messages
352
400
  │ ├─ Context bleed gate: embedding similarity blocks stale context on topic shifts
353
401
  │ ├─ Retry on _general: if tagger returns only _general, retry with expanded context
354
402
  │ ├─ Authoritative tags written to TurnTagIndex (vocabulary-building)
355
- │ ├─ Fact signal extraction: lightweight subject/verb/object triples per turn
356
403
  │ ├─ Related tags generated for cross-vocabulary retrieval
357
404
  │ └─ Compactor generates related_tags at write time (vocabulary bridging)
358
405
  │
359
406
  ▼
360
- Fact curation (on inbound, before assembly)
361
- │ └─ LLM scores retrieved facts for relevance to current query
362
- │ Low-relevance facts dropped before assembly
363
- │
364
- ▼
365
407
  Check token thresholds (soft 70%, hard 85%)
366
408
  │
367
409
  ▼ (if threshold exceeded)
@@ -371,14 +413,14 @@ Segment by tag → summarize each segment (concurrent, ThreadPoolExecutor)
371
413
  │ ├─ Stub segments: media/attachment stubs get passthrough (no LLM), inherit neighbor's tags
372
414
  │ ├─ XML-tagged prev_context: structural separation prevents context leak into summaries
373
415
  │ ├─ Tags preserved: LLM can ADD refined/related tags but never REMOVE originals
374
- │ ├─ Fact consolidation: per-turn fact signals → structured Fact records with provenance
416
+ │ ├─ Fact extraction: delete-and-replace per segment, code_mode filters investigatory noise
375
417
  │ └─ Related tags written into stored segments for future cross-vocabulary retrieval
376
418
  │
377
419
  ▼
378
420
  Compute greedy set cover → build/update per-tag summaries (Layer 2)
379
421
  │
380
422
  ▼
381
- Persist engine state (TurnTagIndex + compaction watermark → store)
423
+ Persist engine state (TurnTagIndex + compaction watermark → store + Redis)
382
424
  ```
383
425
 
384
426
  ## Key Capabilities
@@ -445,7 +487,7 @@ For proxy/OpenClaw conversations, session dates come from envelope metadata time
445
487
 
446
488
  ### Context Awareness Hints
447
489
 
448
- After compaction, the LLM loses visibility into what topics have been stored. virtual-context injects a lightweight `<context-topics>` block into the system prompt:
490
+ After compaction, the LLM loses visibility into what topics have been stored. virtual-context injects a lightweight `<context-topics>` block into the last user message (not the system prompt, so the system prompt remains stable and cacheable):
449
491
 
450
492
  ```xml
451
493
  <context-topics>
@@ -463,9 +505,9 @@ This costs ~50-200 tokens and enables a natural drill-down loop: the user asks f
463
505
 
464
506
  Summaries compress information but inevitably lose specific details. When the user says "I run 5K every morning" at turn 14, a summary might retain "runs regularly" but drop the exact distance and timing. Most memory systems extract facts in a single LLM pass and trust the output directly: raw text goes in, extracted facts come out, and those facts are stored as-is. virtual-context takes a fundamentally different approach with a two-phase pipeline where per-turn signals are treated as hints, not ground truth.
465
507
 
466
- **Phase 1: Fact signals (per-turn).** The response tagger extracts lightweight subject/verb/object triples from each turn as it's processed, with full surrounding context (the same context lookback and bleed gating used for tagging). "I run 5K every morning" becomes `{subject: "user", verb: "runs", object: "5K every morning"}`. These are fast, cheap, and stored on the TurnTagIndex. Critically, they are not yet committed as permanent facts.
508
+ **Fact extraction (at compaction).** Facts are extracted from the full turn group when the compactor processes a segment, not per-turn during ingestion. This produces higher-quality facts because the LLM sees the complete conversation flow across multiple turns: what the user asked, how the assistant responded, what was clarified or corrected. The result is a structured `Fact` with full provenance: subject, verb, what (the core assertion), `fact_type` classification (`preference`, `biographical`, `decision`, `plan`, `opinion`, `routine`, `relationship`, `skill`, `medical`, `financial`, `general`), temporal status (active/completed/planned/abandoned/recurring), associated tags, session ID, and source turn numbers. Facts are stored in dedicated SQLite tables with indexes for efficient querying.
467
509
 
468
- **Phase 2: Fact consolidation (at compaction).** When segments are compacted, per-turn fact signals are verified and consolidated into structured `Fact` records with the full multi-turn segment as context. The consolidation pass can see the complete conversation flow across multiple turns: what the user asked, how the assistant responded, what was clarified or corrected. This means a fact signal from turn 14 gets validated against turns 12-18 before becoming a permanent record. The result is a structured `Fact` with full provenance: subject, verb, what (the core assertion), `fact_type` classification (`preference`, `biographical`, `decision`, `plan`, `opinion`, `routine`, `relationship`, `skill`, `medical`, `financial`, `general`), temporal status (active/completed/planned/abandoned/recurring), associated tags, session ID, and source turn numbers. Facts are stored in dedicated SQLite tables with indexes for efficient querying.
510
+ **Delete-and-replace on re-compaction.** When a segment is re-compacted (e.g., after new turns are added to an existing topic), all facts for that segment are atomically deleted and re-extracted from the full turn group. This prevents fact duplication across re-compaction cycles and ensures facts always reflect the latest understanding. A `code_mode` prompt modifier filters investigatory noise from coding conversations (e.g., "assistant examined file X") and focuses extraction on outcomes: what was built, fixed, changed, or decided.
469
511
 
470
512
  **Why two phases matter.** A single-pass extractor processing "yes, let's go with PostgreSQL" in isolation has no idea what "yes" refers to. It might extract nothing, or hallucinate a fact. virtual-context's response tagger sees the surrounding turns ("Should we use PostgreSQL or MySQL for the user table?") and generates the correct signal. The consolidation pass then verifies it against the full segment before storing a permanent fact. Two chances to get it right, each with progressively more context.
471
513
 
@@ -488,6 +530,61 @@ vc_query_facts(fact_type="preference")
488
530
 
489
531
  **Semantic fact search.** When structured filters return sparse results, a fallback embedding search matches the query intent against all stored facts' `what` fields by cosine similarity, surfacing relevant facts even when the subject/verb/object decomposition doesn't align.
490
532
 
533
+ ### Chain Collapse and Tool Compression
534
+
535
+ Agent conversations are dominated by tool calls. A coding session with 50 tool rounds might have 900K tokens of tool output but only 60K of actual conversation. Raw tool output (file contents, search results, command output) is high-volume, low-reuse information that crushes the context window.
536
+
537
+ virtual-context collapses entire tool chains into compact stubs:
538
+
539
+ ```
540
+ Before (3 messages, ~18K tokens):
541
+ assistant: [tool_use: Read file.py]
542
+ user: [tool_result: <full 500-line file contents>]
543
+ assistant: "The file has a bug on line 42..."
544
+
545
+ After (2 messages, ~200 tokens):
546
+ user: [compacted turn — tool activity: Read(file.py) — vc_restore_tool can recover full content]
547
+ assistant: "The file has a bug on line 42..."
548
+ ```
549
+
550
+ Chain collapse handles all four provider formats (Anthropic `tool_use`/`tool_result`, OpenAI Chat `tool_calls`/`role:tool`, OpenAI Responses `function_call`/`function_call_output`, Gemini `functionCall`/`functionResponse`). Full raw tool output is stored durably with content-addressed refs and recoverable via `vc_restore_tool`.
551
+
552
+ **Deep compaction** drops stubs entirely past a configurable age threshold (`deep_compaction_ratio`). A stub from turn 5 in a 200-turn conversation adds no value; the segment summaries already cover that content.
553
+
554
+ ### Media Compression
555
+
556
+ Base64 images in API payloads are enormous: a single screenshot is 300-500KB of base64, consuming ~100K tokens. Multimodal conversations with dozens of images can reach 10MB+.
557
+
558
+ virtual-context compresses images on first sight, before any pipeline processing:
559
+
560
+ - Detect base64 images across all 4 formats
561
+ - Compress to JPEG at configurable quality, store originals to disk
562
+ - Replace in-flight payload with compressed version
563
+ - A 391KB screenshot → ~40KB compressed, saving ~88K tokens per image
564
+ - Recovery via `vc_restore_tool` returns the original uncompressed content
565
+
566
+ Media compression runs on both passthrough and active paths, so even conversations that haven't triggered compaction benefit.
567
+
568
+ ### Fill Pass
569
+
570
+ After chain collapse and budget enforcement compress the payload, the context window may have significant unused capacity. The fill pass replenishes it with high-value content from the store:
571
+
572
+ - **Phase 1a**: Overflow tag summaries — topics that didn't fit during initial assembly
573
+ - **Phase 1b**: Breadth summaries — sample from remaining tags for broader coverage
574
+ - **Phase 2**: Recent turns from store — newest first, filling toward a soft target threshold
575
+
576
+ The fill pass runs after all compression, so it never fights against budget enforcement. When clients truncate history (detected via `payload_turns < store_turns * 0.70`), the fill pass works with store-backed recovery to restore the most valuable context.
577
+
578
+ ### Store-Backed Recovery
579
+
580
+ Clients (Claude Code, OpenClaw) sometimes truncate conversation history to manage their own context windows. When this happens, virtual-context detects the truncation and recovers from its durable store:
581
+
582
+ - **Chain snapshot recovery**: Restore compact tool chain stubs from stored `chain_snapshots`
583
+ - **Turn recovery**: Restore recent raw turns from stored `turn_messages`
584
+ - **Sanitization**: Strip thinking blocks, replace media with passive placeholders, remove orphaned tool scaffolding
585
+
586
+ Recovery is transparent to the client. The payload that reaches the LLM contains the recovered context as if it had never been truncated.
587
+
491
588
  ### Virtual Memory Paging
492
589
 
493
590
  RAG retrieves content and appends it to the context window. It never frees space from what's already there. When a 100k document needs to enter a 120k window that already has 60k of conversation history, RAG has three options: truncate (lossy), error (useless), or chunk (every chunking approach either costs extra user turns, loses cross-chunk coherence, or both). Nobody touches the existing 60k. It sits there, potentially full of stale context from 30 turns ago that nobody needs anymore.
@@ -639,10 +736,14 @@ virtual-context transform -m "What about auth?"# tag + retrieve + assemble
639
736
  virtual-context aliases list # show all tag aliases
640
737
  virtual-context aliases suggest # auto-detect potential aliases
641
738
  virtual-context aliases add db database # register alias manually
739
+ virtual-context init coding # create config from preset
740
+ virtual-context onboard # guided setup (create/validate config)
741
+ virtual-context onboard --wizard # interactive multi-instance setup
642
742
  virtual-context proxy -u https://api.anthropic.com # single-instance proxy
643
743
  virtual-context proxy # multi-instance (from config)
644
744
  virtual-context presets list # list available config presets
645
745
  virtual-context presets show coding # dump preset config as YAML
746
+ virtual-context daemon install # install daemon/service
646
747
  virtual-context daemon status # service status (platform-specific)
647
748
  virtual-context daemon start # start/enable daemon
648
749
  virtual-context daemon stop # stop daemon
@@ -686,19 +787,23 @@ virtual-context chat --replay vc-session.json
686
787
 
687
788
  ### Proxy Deep Dive
688
789
 
689
- **Session continuity.** The proxy injects an invisible `<!-- vc:session=UUID -->` marker into every assistant response. On subsequent requests, the proxy extracts the marker, routes to the correct session, and strips markers before forwarding upstream. If the proxy restarts, it loads persisted engine state from the store. Multiple concurrent conversations are routed independently via a session registry.
790
+ **Conversation continuity.** The proxy injects an invisible `<!-- vc:conversation=UUID -->` marker into every assistant response. On subsequent requests, the proxy extracts the first marker in the conversation history, routes to the correct conversation, and strips markers before forwarding upstream. Stable conversation identity is derived from a format-specific hash of the system prompt and early messages, so the same client session always routes to the same conversation even across restarts. Valid UUID markers in the payload are accepted as-is without re-hashing.
791
+
792
+ **Redis session cache.** A write-through Redis cache persists conversation history and engine state across container restarts. On startup, conversations are restored from Redis with full history, eliminating cold-start re-ingestion. Degraded mode: if Redis is unavailable, the proxy falls back to store-only persistence with no interruption.
690
793
 
691
794
  **Conversation-scoped retrieval.** All store retrieval methods are scoped by `conversation_id`. Multiple conversations sharing the same SQLite database are fully isolated; a new conversation never gets context from another conversation's segments.
692
795
 
693
- **Session suppression.** When a session has no compacted data, the pipeline is suppressed; requests pass through as-is. Once the first compaction runs, the pipeline activates automatically.
796
+ **Pipeline suppression.** When a conversation has no compacted data, the pipeline is suppressed; requests pass through as-is. Once the first compaction runs, the pipeline activates automatically.
694
797
 
695
798
  **History ingestion.** On the first request, the proxy extracts user+assistant pairs from the client's existing conversation history and tags each to bootstrap the TurnTagIndex. No cold-start period.
696
799
 
697
- **Format-agnostic.** Auto-detects Anthropic, OpenAI (Chat + Codex/Responses), and Gemini request formats. Context is injected into the appropriate location per format. A single proxy instance handles all formats on one port.
800
+ **Four-format support.** Auto-detects Anthropic, OpenAI Chat, OpenAI Responses, and Gemini request formats. Every pipeline stage (chain collapse, media compression, budget enforcement, context injection, stub generation, token counting) is format-aware through the `PayloadFormat` abstraction. A single proxy instance handles all formats on one port.
801
+
802
+ **Image-aware token counting.** Base64 images are counted using the Anthropic formula `(width × height) / 750` after scaling to 1568px max dimension, instead of tokenizing the raw base64 string. This prevents massive over-counting (a 10MB image payload is ~107K tokens, not 7M) and ensures budget enforcement and tier checks operate on accurate numbers.
698
803
 
699
804
  **Streaming with zero added latency.** SSE streams are forwarded byte-for-byte. Text deltas are accumulated in the background for response tagging.
700
805
 
701
- **Error-resilient.** If the engine fails, the request is forwarded to upstream unmodified. The proxy never blocks your LLM calls.
806
+ **Error-resilient.** If the engine fails, the request is forwarded to upstream unmodified. The proxy never blocks your LLM calls. If VC enrichment produces a larger payload than the original, the bloat fallback reverts to a pure passthrough with the original client body.
702
807
 
703
808
  **Envelope stripping + metadata extraction.** Strips client metadata while extracting sender identity and timestamps from labeled JSON blocks. Group chat participants appear as "Sania" and "Yur" instead of generic "User". Original message timestamps give segments accurate chronological ordering.
704
809
 
@@ -756,8 +861,13 @@ Plugin for OpenClaw agents using lifecycle hooks for sync retrieval (`message.pr
756
861
  | **ProxyServer** | `proxy/server.py` | HTTP proxy factory (`create_app`), delegates to state/registry/handlers |
757
862
  | **ProxyState** | `proxy/state.py` | Session state machine: ingestion, tagging, compaction lifecycle |
758
863
  | **SessionRegistry** | `proxy/registry.py` | Multi-session routing with fingerprint matching |
864
+ | **MessageFilter** | `proxy/message_filter.py` | Chain collapse, turn grouping, budget enforcement, fill pass, upstream trim |
865
+ | **TokenCounter** | `token_counter.py` | Image-aware token counting (Anthropic formula for images, tiktoken/estimate for text) |
866
+ | **MediaCompressor** | `proxy/media.py` | Base64 image compression, disk storage, intrusion detection |
867
+ | **ConversationIdentity** | `conversation_identity.py` | Stable per-format conversation ID hashing, marker extraction |
759
868
  | **ProxyHandlers** | `proxy/handlers.py` | Streaming/non-streaming/passthrough HTTP request handlers |
760
869
  | **MultiInstance** | `proxy/multi.py` | Multi-instance launcher: N uvicorn listeners, shared or per-port engine/store |
870
+ | **RedisSessionCache** | `proxy/redis_cache.py` | Write-through history cache for lossless restarts |
761
871
  | **ProxyDashboard** | `proxy/dashboard.py` | Live SSE dashboard with request grid, turn inspector, session stats (auth-gated mutations) |
762
872
  | **ProxyMetrics** | `proxy/metrics.py` | Thread-safe event collector with bounded deque + request capture ring buffer |
763
873
 
@@ -806,7 +916,7 @@ Both providers reuse a persistent `httpx.Client` across calls (connection poolin
806
916
 
807
917
  **Tag preservation.** During compaction, the LLM can add refined tags but never remove original ones. A segment tagged `[ux, recipes, frontend]` stays tagged with all three even after summarization, ensuring cross-topic retrieval always works.
808
918
 
809
- **Tool chain integrity.** The history filter preserves API-required message dependencies atomically. Every `tool_use` block in an assistant message is kept with its corresponding `tool_result`, and vice versa. Forward and backward scanning ensures multi-step tool chains are never broken, even when surrounding turns are filtered out.
919
+ **Tool chain collapse.** Historical tool chains (assistant `tool_use` → user `tool_result` → assistant response, and their OpenAI/Gemini equivalents) are collapsed into compact stubs containing tool names, truncated previews, and content-addressed restore refs. This is the single largest compression lever: a 937K-token payload with 52 tool chains collapses to ~65K. Full tool output is stored durably and recoverable via `vc_restore_tool`. Deep compaction drops stubs entirely past a configurable age threshold. The history filter preserves API-required message dependencies atomically; tool chains are never partially broken.
810
920
 
811
921
  **The virtual memory analogy is literal, not metaphorical.** Every component in VC maps to a systems-level equivalent:
812
922