virtual-context 0.2.9__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (232) hide show
  1. {virtual_context-0.2.9 → virtual_context-0.3.1}/PKG-INFO +175 -30
  2. virtual_context-0.3.1/README-draft.md +519 -0
  3. {virtual_context-0.2.9 → virtual_context-0.3.1}/README.md +173 -28
  4. virtual_context-0.3.1/README.md.backup +1130 -0
  5. {virtual_context-0.2.9 → virtual_context-0.3.1}/pyproject.toml +2 -2
  6. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_cli_init.py +67 -11
  7. virtual_context-0.3.1/tests/test_engine_sync_turns.py +195 -0
  8. virtual_context-0.3.1/tests/test_session_state.py +148 -0
  9. virtual_context-0.3.1/tests/test_vcattach.py +121 -0
  10. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/__init__.py +1 -1
  11. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/cli/main.py +21 -4
  12. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/config.py +9 -1
  13. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/composite_store.py +14 -0
  14. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/protocols.py +2 -0
  15. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/quote_search.py +11 -0
  16. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/tagging_pipeline.py +14 -4
  17. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/engine.py +369 -60
  18. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/formats.py +452 -65
  19. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/handlers.py +105 -0
  20. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/message_filter.py +4 -4
  21. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/registry.py +10 -0
  22. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/server.py +75 -18
  23. virtual_context-0.3.1/virtual_context/proxy/session_state.py +413 -0
  24. virtual_context-0.3.1/virtual_context/proxy/vcattach.py +97 -0
  25. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/storage/filesystem.py +30 -0
  26. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/storage/postgres.py +69 -0
  27. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/storage/sqlite.py +63 -0
  28. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/types.py +4 -0
  29. {virtual_context-0.2.9 → virtual_context-0.3.1}/.gitignore +0 -0
  30. {virtual_context-0.2.9 → virtual_context-0.3.1}/LICENSE +0 -0
  31. {virtual_context-0.2.9 → virtual_context-0.3.1}/assets/dashboard.png +0 -0
  32. {virtual_context-0.2.9 → virtual_context-0.3.1}/assets/hero.png +0 -0
  33. {virtual_context-0.2.9 → virtual_context-0.3.1}/models.yaml +0 -0
  34. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/REGRESSION_MAP.md +0 -0
  35. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/conftest.py +0 -0
  36. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/docker-compose.test.yml +0 -0
  37. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/haiku/__init__.py +0 -0
  38. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/haiku/conftest.py +0 -0
  39. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/haiku/test_compaction.py +0 -0
  40. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/haiku/test_retrieval.py +0 -0
  41. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/haiku/test_tagging.py +0 -0
  42. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/ollama/__init__.py +0 -0
  43. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/ollama/conftest.py +0 -0
  44. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/ollama/test_compactor.py +0 -0
  45. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/ollama/test_pipeline.py +0 -0
  46. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/ollama/test_provider.py +0 -0
  47. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/ollama/test_tag_generator.py +0 -0
  48. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/proxy/__init__.py +0 -0
  49. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/proxy/test_dashboard_cors.py +0 -0
  50. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/proxy/test_metrics.py +0 -0
  51. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_assembler.py +0 -0
  52. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_backend_integration.py +0 -0
  53. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_budget_enforcement.py +0 -0
  54. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_compaction_commit_prune.py +0 -0
  55. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_compactor.py +0 -0
  56. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_compactor_concurrent.py +0 -0
  57. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_composite_store.py +0 -0
  58. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_config.py +0 -0
  59. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_context_bleed.py +0 -0
  60. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_conversation_identity.py +0 -0
  61. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_conversation_lifecycle.py +0 -0
  62. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_conversation_scoping.py +0 -0
  63. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_embedding_tag_generator.py +0 -0
  64. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_empty_turn_skip.py +0 -0
  65. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_engine_integration.py +0 -0
  66. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_engine_lookback.py +0 -0
  67. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_engine_state.py +0 -0
  68. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_fact_enrichment.py +0 -0
  69. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_fact_graph_integration.py +0 -0
  70. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_fact_link_checker.py +0 -0
  71. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_fact_link_query.py +0 -0
  72. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_fact_link_types.py +0 -0
  73. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_fact_links_sqlite.py +0 -0
  74. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_fact_redesign.py +0 -0
  75. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_fill_pass.py +0 -0
  76. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_find_quote.py +0 -0
  77. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_format_agnostic.py +0 -0
  78. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_headless.py +0 -0
  79. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_history_filter.py +0 -0
  80. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_idf_retrieval.py +0 -0
  81. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_ingest_index_integrity.py +0 -0
  82. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_longmemeval_auth.py +0 -0
  83. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_mcp_server.py +0 -0
  84. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_media.py +0 -0
  85. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_message_filter.py +0 -0
  86. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_metrics_persistence.py +0 -0
  87. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_model_catalog.py +0 -0
  88. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_model_limits.py +0 -0
  89. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_monitor.py +0 -0
  90. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_multi_instance.py +0 -0
  91. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_noop_fact_link_store.py +0 -0
  92. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_openrouter_provider.py +0 -0
  93. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_paging.py +0 -0
  94. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_passthrough_filter.py +0 -0
  95. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_presets.py +0 -0
  96. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_prev_context_leak.py +0 -0
  97. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_provider_adapters.py +0 -0
  98. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_proxy.py +0 -0
  99. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_proxy_dashboard.py +0 -0
  100. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_proxy_formats.py +0 -0
  101. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_proxy_message_filter.py +0 -0
  102. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_proxy_session.py +0 -0
  103. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_proxy_streaming.py +0 -0
  104. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_raw_content.py +0 -0
  105. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_recall_all.py +0 -0
  106. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_registry_lifecycle.py +0 -0
  107. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_request_captures_persistence.py +0 -0
  108. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_retriever.py +0 -0
  109. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_rrf_scoring.py +0 -0
  110. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_segmenter.py +0 -0
  111. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_semantic_search.py +0 -0
  112. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_sender_identity.py +0 -0
  113. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_session_cache.py +0 -0
  114. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_session_date.py +0 -0
  115. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_storage_protocols.py +0 -0
  116. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_store_recovery.py +0 -0
  117. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_store_sqlite.py +0 -0
  118. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_stub_turn_handling.py +0 -0
  119. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_supersession.py +0 -0
  120. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_supersession_migration.py +0 -0
  121. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_tag_canonicalizer.py +0 -0
  122. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_tag_consolidator.py +0 -0
  123. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_tag_generator.py +0 -0
  124. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_tag_splitter.py +0 -0
  125. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_telemetry.py +0 -0
  126. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_telemetry_integration.py +0 -0
  127. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_tool_loop.py +0 -0
  128. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_tool_output_interceptor.py +0 -0
  129. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_tool_result_filter.py +0 -0
  130. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_tool_tags.py +0 -0
  131. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_tui.py +0 -0
  132. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_turn_grouping.py +0 -0
  133. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_turn_tag_index.py +0 -0
  134. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_unified_budget.py +0 -0
  135. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_upstream_trim.py +0 -0
  136. {virtual_context-0.2.9 → virtual_context-0.3.1}/tests/test_verb_expansion.py +0 -0
  137. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual-context.yaml +0 -0
  138. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual-context.yaml.example +0 -0
  139. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/cli/__init__.py +0 -0
  140. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/conversation_identity.py +0 -0
  141. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/__init__.py +0 -0
  142. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/assembler.py +0 -0
  143. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/compaction_pipeline.py +0 -0
  144. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/compactor.py +0 -0
  145. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/conversation_store.py +0 -0
  146. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/embedding_provider.py +0 -0
  147. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/embedding_tag_generator.py +0 -0
  148. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/engine_utils.py +0 -0
  149. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/fact_query.py +0 -0
  150. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/fts_preprocessor.py +0 -0
  151. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/hint_builder.py +0 -0
  152. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/llm_utils.py +0 -0
  153. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/math_utils.py +0 -0
  154. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/model_catalog.py +0 -0
  155. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/monitor.py +0 -0
  156. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/paging_manager.py +0 -0
  157. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/provider_adapters.py +0 -0
  158. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/retrieval_assembler.py +0 -0
  159. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/retrieval_scoring.py +0 -0
  160. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/retriever.py +0 -0
  161. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/search_engine.py +0 -0
  162. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/segmenter.py +0 -0
  163. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/semantic_search.py +0 -0
  164. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/store.py +0 -0
  165. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/tag_canonicalizer.py +0 -0
  166. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/tag_consolidator.py +0 -0
  167. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/tag_generator.py +0 -0
  168. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/tag_scoring.py +0 -0
  169. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/tag_splitter.py +0 -0
  170. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/telemetry.py +0 -0
  171. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/temporal_resolver.py +0 -0
  172. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/tool_loop.py +0 -0
  173. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/tool_query.py +0 -0
  174. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/core/turn_tag_index.py +0 -0
  175. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/data/anthropic-tokenizer/tokenizer.json +0 -0
  176. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/ingest/__init__.py +0 -0
  177. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/ingest/curator.py +0 -0
  178. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/ingest/date_resolver.py +0 -0
  179. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/ingest/parsers.py +0 -0
  180. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/ingest/supersession.py +0 -0
  181. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/mcp/__init__.py +0 -0
  182. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/mcp/server.py +0 -0
  183. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/model_limits.py +0 -0
  184. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/openclaw/virtual-context.mjs +0 -0
  185. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/patterns.py +0 -0
  186. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/presets/__init__.py +0 -0
  187. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/presets/agentic.py +0 -0
  188. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/presets/base.py +0 -0
  189. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/presets/coding.py +0 -0
  190. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/providers/__init__.py +0 -0
  191. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/providers/anthropic.py +0 -0
  192. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/providers/base.py +0 -0
  193. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/providers/generic_openai.py +0 -0
  194. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/providers/ollama_native.py +0 -0
  195. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/__init__.py +0 -0
  196. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/_envelope.py +0 -0
  197. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/dashboard.html +0 -0
  198. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/dashboard.py +0 -0
  199. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/helpers.py +0 -0
  200. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/media.py +0 -0
  201. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/metrics.py +0 -0
  202. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/multi.py +0 -0
  203. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/session_cache.py +0 -0
  204. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/state.py +0 -0
  205. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/static/android-chrome-192x192.png +0 -0
  206. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/static/android-chrome-512x512.png +0 -0
  207. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/static/apple-touch-icon.png +0 -0
  208. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/static/favicon-16x16.png +0 -0
  209. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/static/favicon-32x32.png +0 -0
  210. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/static/favicon.ico +0 -0
  211. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/static/site.webmanifest +0 -0
  212. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/proxy/tool_output_interceptor.py +0 -0
  213. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/storage/__init__.py +0 -0
  214. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/storage/falkordb.py +0 -0
  215. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/storage/helpers.py +0 -0
  216. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/storage/neo4j.py +0 -0
  217. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/storage/noop_fact_link_store.py +0 -0
  218. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/token_counter.py +0 -0
  219. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/__init__.py +0 -0
  220. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/app.py +0 -0
  221. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/chat.tcss +0 -0
  222. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/chat_provider.py +0 -0
  223. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/headless.py +0 -0
  224. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/modals/__init__.py +0 -0
  225. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/modals/turn_inspector.py +0 -0
  226. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/state.py +0 -0
  227. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/widgets/__init__.py +0 -0
  228. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/widgets/budget_bar.py +0 -0
  229. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/widgets/chat_view.py +0 -0
  230. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/widgets/input_box.py +0 -0
  231. {virtual_context-0.2.9 → virtual_context-0.3.1}/virtual_context/tui/widgets/tag_panel.py +0 -0
  232. {virtual_context-0.2.9 → virtual_context-0.3.1}/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.9
3
+ Version: 0.3.1
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
@@ -16,7 +16,7 @@ Requires-Python: >=3.11
16
16
  Requires-Dist: anthropic>=0.40
17
17
  Requires-Dist: dateparser>=1.2
18
18
  Requires-Dist: fastapi>=0.115
19
- Requires-Dist: httpx>=0.27
19
+ Requires-Dist: httpx[http2]>=0.27
20
20
  Requires-Dist: mcp>=1.0
21
21
  Requires-Dist: openai>=1.50
22
22
  Requires-Dist: pillow>=10.0
@@ -45,6 +45,13 @@ Provides-Extra: redis
45
45
  Requires-Dist: redis>=5.0; extra == 'redis'
46
46
  Description-Content-Type: text/markdown
47
47
 
48
+ <!-- [![PyPI](https://img.shields.io/pypi/v/virtual-context.svg)](https://pypi.org/project/virtual-context/) -->
49
+ <!-- [![Python](https://img.shields.io/pypi/pyversions/virtual-context.svg)](https://pypi.org/project/virtual-context/) -->
50
+ <!-- [![Downloads](https://img.shields.io/pypi/dm/virtual-context.svg)](https://pypistats.org/packages/virtual-context) -->
51
+ <!-- [![License](https://img.shields.io/badge/license-AGPL--3.0-blue.svg)](https://github.com/yursilkidwai/virtual-context/blob/main/LICENSE) -->
52
+ [![Discord](https://img.shields.io/badge/Discord-Chat%20with%20us-5865F2?logo=discord&logoColor=white)](https://discord.gg/YxDHKEZz)
53
+ [![Twitter](https://img.shields.io/badge/Twitter-Follow-1DA1F2?logo=x&logoColor=white)](https://x.com/virtualctx)
54
+
48
55
  <p align="center">
49
56
  <a href="assets/dashboard.png">
50
57
  <img src="assets/dashboard.png" alt="virtual-context dashboard" width="800">
@@ -180,7 +187,7 @@ proxy:
180
187
  **Daemon mode:** run as a background service:
181
188
 
182
189
  ```bash
183
- # Creates config if needed, installs + starts daemon
190
+ # Creates ~/.virtualcontext/ with config + data, installs + starts daemon
184
191
  virtual-context daemon install --upstream https://api.anthropic.com
185
192
 
186
193
  # Or: guided interactive setup with daemon
@@ -282,6 +289,7 @@ Exposes virtual-context as an MCP server for integration with Claude Desktop, Cu
282
289
  | Tool | `collapse_topic` | Collapse a topic back to summary or none |
283
290
  | Tool | `find_quote` | Full-text search across all stored conversation text |
284
291
  | Tool | `query_facts` | Structured fact lookup with subject/verb/object/status filters |
292
+ | Tool | `restore_tool` | Recover full content from compacted chain stubs or compressed media |
285
293
  | Resource | `virtualcontext://domains` | List all tags |
286
294
  | Resource | `virtualcontext://domains/{tag}` | Summaries for a specific tag |
287
295
  | Prompt | `recall` | Suggest context retrieval for a topic |
@@ -293,11 +301,12 @@ Exposes virtual-context as an MCP server for integration with Claude Desktop, Cu
293
301
  User message arrives
294
302
  │
295
303
  ▼
296
- Session routing (proxy mode)
297
- │ ├─ Extract session ID from <!-- vc:session=UUID --> markers in assistant messages
298
- │ ├─ Route to existing session or load persisted state from store
299
- │ ├─ No marker? → reuse default session (first request) or create new
300
- │ └─ Strip session markers before forwarding to upstream
304
+ Conversation routing (proxy mode)
305
+ │ ├─ Extract conversation ID from <!-- vc:conversation=UUID --> markers
306
+ │ ├─ Route to existing conversation or load persisted state from store + Redis
307
+ │ ├─ No marker? → derive stable ID from system prompt hash + format
308
+ │ ├─ Redis session cache: lossless restart, write-through history persistence
309
+ │ └─ Strip conversation markers before forwarding upstream
301
310
  │
302
311
  ▼
303
312
  Strip client envelope + extract metadata
@@ -307,13 +316,20 @@ Strip client envelope + extract metadata
307
316
  │ └─ Metadata preserved on Message.metadata for downstream use
308
317
  │
309
318
  ▼
319
+ Media compression (all paths — passthrough and active)
320
+ │ ├─ Detect base64 images across all 4 formats (Anthropic, OpenAI Chat, Responses, Gemini)
321
+ │ ├─ Compress to JPEG, store originals to disk for later recovery
322
+ │ ├─ Image token counting uses Anthropic formula: (width × height) / 750
323
+ │ └─ A 391KB screenshot → ~40KB compressed, saving ~88k tokens
324
+ │
325
+ ▼
310
326
  History ingestion (first request only)
311
327
  │ ├─ Extract and tag all prior user+assistant pairs → bootstrap TurnTagIndex
312
328
  │ ├─ Stub detection: media attachments/image placeholders get _stub tag (skip LLM tagger)
313
329
  │ └─ Conversation-scoped: each conversation's index is independent
314
330
  │
315
331
  ▼
316
- Inbound tagging - identify what this message is about
332
+ Inbound tagging — identify what this message is about
317
333
  │ ├─ Embedding tagger (recommended): cosine similarity against existing tag vocabulary
318
334
  │ │ (closed-set, deterministic, can't hallucinate novel tags)
319
335
  │ ├─ LLM / keyword tagger: alternative with vocabulary feedback
@@ -335,39 +351,66 @@ Assemble context within token budget
335
351
  │ └─ Tag sections: retrieved summaries ordered by tag priority
336
352
  │
337
353
  ▼
354
+ Chain collapse — compress tool-bearing history turns
355
+ │ ├─ Group raw messages into logical turns via group_into_turns()
356
+ │ ├─ Tool chains (assistant tool_use → user tool_result → assistant) → compact stubs
357
+ │ ├─ Stubs contain tool names, truncated previews, and restore refs
358
+ │ ├─ Handles all 4 formats: Anthropic, OpenAI Chat, OpenAI Responses, Gemini
359
+ │ ├─ Deep compaction: drops stubs entirely past configurable age threshold
360
+ │ └─ Non-tool turns outside protected window are dropped (summaries cover them)
361
+ │
362
+ ▼
363
+ Store-backed recovery (when client truncates history)
364
+ │ ├─ Detect truncation: payload turns < 70% of stored turns
365
+ �� ├─ Recover chain snapshots from durable store (compact stubs with metadata)
366
+ │ ├─ Recover recent turns from stored turn_messages
367
+ │ └─ Sanitize restored turns: strip thinking blocks, replace media with placeholders
368
+ │
369
+ ▼
338
370
  Filter conversation history
339
371
  │ ├─ Drop turns whose tags don't overlap with inbound tags
340
372
  │ ├─ Preserve tool chains atomically (tool_use ↔ tool_result never separated)
373
+ │ ├─ Protected zone intrusion: stub tool results in protected turns 3+ when zone exceeds budget %
341
374
  │ ├─ Protect recent turns (always kept regardless of tags)
342
375
  │ └─ Temporal queries skip filtering entirely
343
376
  │
344
377
  ▼
345
- Inject <virtual-context> block → forward enriched request to LLM
378
+ Budget enforcement — iterative payload reduction
379
+ │ ├─ Scan reducible items: conversation text, tool results, thinking blocks, images
380
+ │ ├─ Cut largest reducible item per iteration until under budget
381
+ │ ├─ Bloat fallback: if VC enrichment exceeds inbound size, fall back to pure passthrough
382
+ │ └─ Upstream trim: final trim to model's actual context window limit
383
+ │
384
+ ▼
385
+ Fill pass — replenish context after compression
386
+ │ ├─ Phase 1a: overflow tag summaries (topics that didn't fit during assembly)
387
+ │ ├─ Phase 1b: breadth summaries (sample from remaining tags)
388
+ │ ├─ Phase 2: recent turns from store (newest first)
389
+ │ └─ Target: soft threshold between floor and budget ceiling
390
+ │
391
+ ▼
392
+ Inject <virtual-context> block into last user message → forward to LLM
393
+ │ (injected into user messages, not system prompt, for Anthropic cache stability)
346
394
  │
347
395
  ▼
348
396
  LLM processes enriched context → produces response
349
397
  │
350
398
  ▼
351
- Inject session marker into response (proxy mode)
352
- │ ├─ Streaming: emit final SSE delta with <!-- vc:session=UUID -->
353
- │ └─ Non-streaming: append marker to last text content block
399
+ Inject conversation marker into response (proxy mode)
400
+ │ ├─ Streaming: emit final SSE delta with <!-- vc:conversation=UUID -->
401
+ │ ├─ Non-streaming: append marker to last text content block
402
+ │ └─ Skip injection when payload already contains a conversation marker
354
403
  │
355
404
  ▼
356
- Response tagging - LLM tags the full user+assistant pair (background thread)
405
+ Response tagging — LLM tags the full user+assistant pair (background thread)
357
406
  │ ├─ Context lookback: feed N recent pairs as tagger context for short/ambiguous messages
358
407
  │ ├─ Context bleed gate: embedding similarity blocks stale context on topic shifts
359
408
  │ ├─ Retry on _general: if tagger returns only _general, retry with expanded context
360
409
  │ ├─ Authoritative tags written to TurnTagIndex (vocabulary-building)
361
- │ ├─ Fact signal extraction: lightweight subject/verb/object triples per turn
362
410
  │ ├─ Related tags generated for cross-vocabulary retrieval
363
411
  │ └─ Compactor generates related_tags at write time (vocabulary bridging)
364
412
  │
365
413
  ▼
366
- Fact curation (on inbound, before assembly)
367
- │ └─ LLM scores retrieved facts for relevance to current query
368
- │ Low-relevance facts dropped before assembly
369
- │
370
- ▼
371
414
  Check token thresholds (soft 70%, hard 85%)
372
415
  │
373
416
  ▼ (if threshold exceeded)
@@ -377,14 +420,14 @@ Segment by tag → summarize each segment (concurrent, ThreadPoolExecutor)
377
420
  │ ├─ Stub segments: media/attachment stubs get passthrough (no LLM), inherit neighbor's tags
378
421
  │ ├─ XML-tagged prev_context: structural separation prevents context leak into summaries
379
422
  │ ├─ Tags preserved: LLM can ADD refined/related tags but never REMOVE originals
380
- │ ├─ Fact consolidation: per-turn fact signals → structured Fact records with provenance
423
+ │ ├─ Fact extraction: delete-and-replace per segment, code_mode filters investigatory noise
381
424
  │ └─ Related tags written into stored segments for future cross-vocabulary retrieval
382
425
  │
383
426
  ▼
384
427
  Compute greedy set cover → build/update per-tag summaries (Layer 2)
385
428
  │
386
429
  ▼
387
- Persist engine state (TurnTagIndex + compaction watermark → store)
430
+ Persist engine state (TurnTagIndex + compaction watermark → store + Redis)
388
431
  ```
389
432
 
390
433
  ## Key Capabilities
@@ -451,7 +494,7 @@ For proxy/OpenClaw conversations, session dates come from envelope metadata time
451
494
 
452
495
  ### Context Awareness Hints
453
496
 
454
- After compaction, the LLM loses visibility into what topics have been stored. virtual-context injects a lightweight `<context-topics>` block into the system prompt:
497
+ 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):
455
498
 
456
499
  ```xml
457
500
  <context-topics>
@@ -469,9 +512,9 @@ This costs ~50-200 tokens and enables a natural drill-down loop: the user asks f
469
512
 
470
513
  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.
471
514
 
472
- **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.
515
+ **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.
473
516
 
474
- **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.
517
+ **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.
475
518
 
476
519
  **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.
477
520
 
@@ -494,6 +537,99 @@ vc_query_facts(fact_type="preference")
494
537
 
495
538
  **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.
496
539
 
540
+ ### Chain Collapse and Tool Compression
541
+
542
+ 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.
543
+
544
+ virtual-context collapses entire tool chains into compact stubs:
545
+
546
+ ```
547
+ Before (3 messages, ~18K tokens):
548
+ assistant: [tool_use: Read file.py]
549
+ user: [tool_result: <full 500-line file contents>]
550
+ assistant: "The file has a bug on line 42..."
551
+
552
+ After (2 messages, ~200 tokens):
553
+ user: [compacted turn — tool activity: Read(file.py) — vc_restore_tool can recover full content]
554
+ assistant: "The file has a bug on line 42..."
555
+ ```
556
+
557
+ 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`.
558
+
559
+ **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.
560
+
561
+ ### Media Compression
562
+
563
+ 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+.
564
+
565
+ virtual-context compresses images on first sight, before any pipeline processing:
566
+
567
+ - Detect base64 images across all 4 formats
568
+ - Compress to JPEG at configurable quality, store originals to disk
569
+ - Replace in-flight payload with compressed version
570
+ - A 391KB screenshot → ~40KB compressed, saving ~88K tokens per image
571
+ - Recovery via `vc_restore_tool` returns the original uncompressed content
572
+
573
+ Media compression runs on both passthrough and active paths, so even conversations that haven't triggered compaction benefit.
574
+
575
+ ### Fill Pass
576
+
577
+ 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:
578
+
579
+ - **Phase 1a**: Overflow tag summaries — topics that didn't fit during initial assembly
580
+ - **Phase 1b**: Breadth summaries — sample from remaining tags for broader coverage
581
+ - **Phase 2**: Recent turns from store — newest first, filling toward a soft target threshold
582
+
583
+ 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.
584
+
585
+ ### Store-Backed Recovery
586
+
587
+ 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:
588
+
589
+ - **Chain snapshot recovery**: Restore compact tool chain stubs from stored `chain_snapshots`
590
+ - **Turn recovery**: Restore recent raw turns from stored `turn_messages`
591
+ - **Sanitization**: Strip thinking blocks, replace media with passive placeholders, remove orphaned tool scaffolding
592
+
593
+ Recovery is transparent to the client. The payload that reaches the LLM contains the recovered context as if it had never been truncated.
594
+
595
+ ### Conversation Identity and VCATTACH
596
+
597
+ Every conversation gets a stable identity derived from the system prompt hash and conversation markers embedded in assistant responses. This identity routes requests to the right conversation's compacted segments, facts, and tags across restarts, deploys, and client changes.
598
+
599
+ When identity detaches — system prompt changes, client truncation loses the marker, a deploy produces a different hash — the user can reattach by typing:
600
+
601
+ ```
602
+ VCATTACH <conversation_id>
603
+ ```
604
+
605
+ The target can be a conversation label (`VCATTACH website`), a full UUID, or a UUID prefix. The proxy intercepts this before reaching the LLM, resolves the target, and returns a response with the correct conversation marker. From the next request onward, the client routes to the target conversation with its full compacted context.
606
+
607
+ **What VCATTACH enables:**
608
+
609
+ **Reattachment after detachment.** The most common case. A deploy or system prompt change creates an orphan conversation. The user types `VCATTACH <label>` and is back on their original conversation with all segments, facts, and tags intact.
610
+
611
+ **Cross-platform shared memory.** A user builds up deep context in Claude Code — architecture decisions, code patterns, debugging history — all compacted into segments and facts. They type `VCATTACH code-project` in a Telegram conversation with a different model and immediately get that full context. Both clients now share the same conversation identity: messages from either platform enrich the same compacted knowledge base. This isn't document sharing or chat mirroring — it's shared memory across platforms and models.
612
+
613
+ **Multi-agent collaboration.** Two agents (or two humans using different clients) can work on the same problem space simultaneously. Agent A researches an RFP in Claude Code, compacting findings into segments. Agent B drafts the proposal in a Telegram group, pulling from the same segments via retrieval. Each agent's contributions are compacted into the shared store. The next agent to query sees everything the other contributed — no manual handoff, no copy-paste, no shared documents. The virtual context IS the shared workspace.
614
+
615
+
616
+ **Client compaction recovery.** When clients manage their own context windows (Claude Code truncates old turns, OpenClaw resets sessions), the conversation marker can be lost. VCATTACH lets the user reconnect to the original conversation. The stored segments and facts survive independently of the client's history.
617
+
618
+ **Conversation merging.** A user accidentally creates two conversations about the same topic. They pick the one with richer context and `VCATTACH` the other to it. The old conversation is deleted; the target keeps all its compacted data.
619
+
620
+ **How it works:**
621
+
622
+ 1. User types `VCATTACH <label_or_id>` as a normal message
623
+ 2. Proxy detects the command (regex on the last user message only — history is inert)
624
+ 3. Resolves target by label (case-insensitive) or UUID (exact or prefix match)
625
+ 4. Registers an alias so old markers redirect permanently
626
+ 5. Deletes the old conversation's engine state
627
+ 6. Resets the target's compaction checkpoints (segments/facts preserved)
628
+ 7. Returns a fake response with the target's conversation marker — no LLM call
629
+ 8. Next request carries the new marker and routes to the target
630
+
631
+ The alias table is persistent — if a stale marker resurfaces from a cached client or different session, it follows the alias instead of creating a new orphan.
632
+
497
633
  ### Virtual Memory Paging
498
634
 
499
635
  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.
@@ -696,19 +832,23 @@ virtual-context chat --replay vc-session.json
696
832
 
697
833
  ### Proxy Deep Dive
698
834
 
699
- **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.
835
+ **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.
836
+
837
+ **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.
700
838
 
701
839
  **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.
702
840
 
703
- **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.
841
+ **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.
704
842
 
705
843
  **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.
706
844
 
707
- **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.
845
+ **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.
846
+
847
+ **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.
708
848
 
709
849
  **Streaming with zero added latency.** SSE streams are forwarded byte-for-byte. Text deltas are accumulated in the background for response tagging.
710
850
 
711
- **Error-resilient.** If the engine fails, the request is forwarded to upstream unmodified. The proxy never blocks your LLM calls.
851
+ **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.
712
852
 
713
853
  **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.
714
854
 
@@ -766,8 +906,13 @@ Plugin for OpenClaw agents using lifecycle hooks for sync retrieval (`message.pr
766
906
  | **ProxyServer** | `proxy/server.py` | HTTP proxy factory (`create_app`), delegates to state/registry/handlers |
767
907
  | **ProxyState** | `proxy/state.py` | Session state machine: ingestion, tagging, compaction lifecycle |
768
908
  | **SessionRegistry** | `proxy/registry.py` | Multi-session routing with fingerprint matching |
909
+ | **MessageFilter** | `proxy/message_filter.py` | Chain collapse, turn grouping, budget enforcement, fill pass, upstream trim |
910
+ | **TokenCounter** | `token_counter.py` | Image-aware token counting (Anthropic formula for images, tiktoken/estimate for text) |
911
+ | **MediaCompressor** | `proxy/media.py` | Base64 image compression, disk storage, intrusion detection |
912
+ | **ConversationIdentity** | `conversation_identity.py` | Stable per-format conversation ID hashing, marker extraction |
769
913
  | **ProxyHandlers** | `proxy/handlers.py` | Streaming/non-streaming/passthrough HTTP request handlers |
770
914
  | **MultiInstance** | `proxy/multi.py` | Multi-instance launcher: N uvicorn listeners, shared or per-port engine/store |
915
+ | **RedisSessionCache** | `proxy/redis_cache.py` | Write-through history cache for lossless restarts |
771
916
  | **ProxyDashboard** | `proxy/dashboard.py` | Live SSE dashboard with request grid, turn inspector, session stats (auth-gated mutations) |
772
917
  | **ProxyMetrics** | `proxy/metrics.py` | Thread-safe event collector with bounded deque + request capture ring buffer |
773
918
 
@@ -816,7 +961,7 @@ Both providers reuse a persistent `httpx.Client` across calls (connection poolin
816
961
 
817
962
  **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.
818
963
 
819
- **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.
964
+ **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.
820
965
 
821
966
  **The virtual memory analogy is literal, not metaphorical.** Every component in VC maps to a systems-level equivalent:
822
967