virtual-context 0.3.0__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.
- {virtual_context-0.3.0 → virtual_context-0.3.1}/PKG-INFO +47 -2
- virtual_context-0.3.1/README-draft.md +519 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/README.md +45 -0
- virtual_context-0.3.1/README.md.backup +1130 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/pyproject.toml +2 -2
- virtual_context-0.3.1/tests/test_engine_sync_turns.py +195 -0
- virtual_context-0.3.1/tests/test_session_state.py +148 -0
- virtual_context-0.3.1/tests/test_vcattach.py +121 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/__init__.py +1 -1
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/composite_store.py +14 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/protocols.py +2 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/quote_search.py +11 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/tagging_pipeline.py +14 -4
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/engine.py +369 -60
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/formats.py +132 -14
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/handlers.py +105 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/registry.py +10 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/server.py +45 -0
- virtual_context-0.3.1/virtual_context/proxy/session_state.py +413 -0
- virtual_context-0.3.1/virtual_context/proxy/vcattach.py +97 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/storage/filesystem.py +30 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/storage/postgres.py +69 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/storage/sqlite.py +63 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/types.py +4 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/.gitignore +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/LICENSE +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/assets/dashboard.png +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/assets/hero.png +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/models.yaml +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/REGRESSION_MAP.md +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/conftest.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/docker-compose.test.yml +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/haiku/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/haiku/conftest.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/haiku/test_compaction.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/haiku/test_retrieval.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/haiku/test_tagging.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/ollama/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/ollama/conftest.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/ollama/test_compactor.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/ollama/test_pipeline.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/ollama/test_provider.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/ollama/test_tag_generator.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/proxy/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/proxy/test_dashboard_cors.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/proxy/test_metrics.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_assembler.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_backend_integration.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_budget_enforcement.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_cli_init.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_compaction_commit_prune.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_compactor.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_compactor_concurrent.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_composite_store.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_config.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_context_bleed.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_conversation_identity.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_conversation_lifecycle.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_conversation_scoping.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_embedding_tag_generator.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_empty_turn_skip.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_engine_integration.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_engine_lookback.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_engine_state.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_fact_enrichment.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_fact_graph_integration.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_fact_link_checker.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_fact_link_query.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_fact_link_types.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_fact_links_sqlite.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_fact_redesign.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_fill_pass.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_find_quote.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_format_agnostic.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_headless.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_history_filter.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_idf_retrieval.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_ingest_index_integrity.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_longmemeval_auth.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_mcp_server.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_media.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_message_filter.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_metrics_persistence.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_model_catalog.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_model_limits.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_monitor.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_multi_instance.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_noop_fact_link_store.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_openrouter_provider.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_paging.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_passthrough_filter.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_presets.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_prev_context_leak.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_provider_adapters.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_proxy.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_proxy_dashboard.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_proxy_formats.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_proxy_message_filter.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_proxy_session.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_proxy_streaming.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_raw_content.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_recall_all.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_registry_lifecycle.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_request_captures_persistence.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_retriever.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_rrf_scoring.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_segmenter.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_semantic_search.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_sender_identity.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_session_cache.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_session_date.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_storage_protocols.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_store_recovery.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_store_sqlite.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_stub_turn_handling.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_supersession.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_supersession_migration.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_tag_canonicalizer.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_tag_consolidator.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_tag_generator.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_tag_splitter.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_telemetry.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_telemetry_integration.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_tool_loop.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_tool_output_interceptor.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_tool_result_filter.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_tool_tags.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_tui.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_turn_grouping.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_turn_tag_index.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_unified_budget.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_upstream_trim.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/tests/test_verb_expansion.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual-context.yaml +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual-context.yaml.example +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/cli/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/cli/main.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/config.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/conversation_identity.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/assembler.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/compaction_pipeline.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/compactor.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/conversation_store.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/embedding_provider.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/embedding_tag_generator.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/engine_utils.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/fact_query.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/fts_preprocessor.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/hint_builder.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/llm_utils.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/math_utils.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/model_catalog.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/monitor.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/paging_manager.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/provider_adapters.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/retrieval_assembler.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/retrieval_scoring.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/retriever.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/search_engine.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/segmenter.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/semantic_search.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/store.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/tag_canonicalizer.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/tag_consolidator.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/tag_generator.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/tag_scoring.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/tag_splitter.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/telemetry.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/temporal_resolver.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/tool_loop.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/tool_query.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/core/turn_tag_index.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/data/anthropic-tokenizer/tokenizer.json +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/ingest/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/ingest/curator.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/ingest/date_resolver.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/ingest/parsers.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/ingest/supersession.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/mcp/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/mcp/server.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/model_limits.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/openclaw/virtual-context.mjs +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/patterns.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/presets/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/presets/agentic.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/presets/base.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/presets/coding.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/providers/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/providers/anthropic.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/providers/base.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/providers/generic_openai.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/providers/ollama_native.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/_envelope.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/dashboard.html +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/dashboard.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/helpers.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/media.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/message_filter.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/metrics.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/multi.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/session_cache.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/state.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/static/android-chrome-192x192.png +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/static/android-chrome-512x512.png +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/static/apple-touch-icon.png +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/static/favicon-16x16.png +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/static/favicon-32x32.png +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/static/favicon.ico +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/static/site.webmanifest +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/proxy/tool_output_interceptor.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/storage/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/storage/falkordb.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/storage/helpers.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/storage/neo4j.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/storage/noop_fact_link_store.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/token_counter.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/app.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/chat.tcss +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/chat_provider.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/headless.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/modals/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/modals/turn_inspector.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/state.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/widgets/__init__.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/widgets/budget_bar.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/widgets/chat_view.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/widgets/input_box.py +0 -0
- {virtual_context-0.3.0 → virtual_context-0.3.1}/virtual_context/tui/widgets/tag_panel.py +0 -0
- {virtual_context-0.3.0 → 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.3.
|
|
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
|
+
<!-- [](https://pypi.org/project/virtual-context/) -->
|
|
49
|
+
<!-- [](https://pypi.org/project/virtual-context/) -->
|
|
50
|
+
<!-- [](https://pypistats.org/packages/virtual-context) -->
|
|
51
|
+
<!-- [](https://github.com/yursilkidwai/virtual-context/blob/main/LICENSE) -->
|
|
52
|
+
[](https://discord.gg/YxDHKEZz)
|
|
53
|
+
[](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">
|
|
@@ -585,6 +592,44 @@ Clients (Claude Code, OpenClaw) sometimes truncate conversation history to manag
|
|
|
585
592
|
|
|
586
593
|
Recovery is transparent to the client. The payload that reaches the LLM contains the recovered context as if it had never been truncated.
|
|
587
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
|
+
|
|
588
633
|
### Virtual Memory Paging
|
|
589
634
|
|
|
590
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.
|
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
<!-- [](https://pypi.org/project/virtual-context/) -->
|
|
2
|
+
<!-- [](https://pypi.org/project/virtual-context/) -->
|
|
3
|
+
<!-- [](https://pypistats.org/packages/virtual-context) -->
|
|
4
|
+
<!-- [](https://github.com/yursilkidwai/virtual-context/blob/main/LICENSE) -->
|
|
5
|
+
[](https://discord.gg/YxDHKEZz)
|
|
6
|
+
[](https://x.com/virtualctx)
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="assets/dashboard.png">
|
|
10
|
+
<img src="assets/dashboard.png" alt="virtual-context dashboard" width="800">
|
|
11
|
+
</a>
|
|
12
|
+
</p>
|
|
13
|
+
<p align="center"><sub>virtual-context cloud: running 3 million virtual token window at 80k actual tokens</sub></p>
|
|
14
|
+
|
|
15
|
+
# virtual-context
|
|
16
|
+
|
|
17
|
+
**100x your agent's context by virtualizing it. Better reasoning. Unlimited memory. Lower costs.**
|
|
18
|
+
|
|
19
|
+
*95% accuracy vs 33% baseline on the same model, at half the cost. [See benchmark →](#benchmark-results)*
|
|
20
|
+
|
|
21
|
+
Your client sets `contextWindow: 20000000` (20 million). Your model's real window is 200K. virtual-context sits between them and makes it work, the same way your OS lets a process address more memory than physically exists. The client sends its full conversation history. VC compresses, indexes, and pages. The model sees a dense 60K window where every token is signal.
|
|
22
|
+
|
|
23
|
+
The result is measurably better reasoning, recall and cost than raw full context.
|
|
24
|
+
|
|
25
|
+
This is what makes virtual-context fundamentally different from memory systems that bolt a vector database onto your LLM. Those systems are *additive*: they retrieve chunks and compete for the context window your agent is working in right now. They are not working to evict or curate the context to what you really need.
|
|
26
|
+
|
|
27
|
+
virtual-context *manages* the window itself: compressing by topic, extracting structured facts, paging in what's needed, and paging out what's not. The client thinks it has 20M tokens. The model sees 60K of curated signal. Nothing is lost. Everything is addressable, at varying levels of compression.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Layer 0: Raw conversation turns (active memory, in the context window)
|
|
31
|
+
Layer 1: Segment summaries + Facts per tag (compressed pages, per-topic summaries)
|
|
32
|
+
Layer 2: Tag summaries via greedy set cover (working set descriptors, bird's-eye view)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The result: an agent that recalls details from turn 12 at turn 1000 with the same fidelity as if the conversation just started.
|
|
36
|
+
|
|
37
|
+
## Cloud Offering
|
|
38
|
+
|
|
39
|
+
[https://virtual-context.com](https://virtual-context.com) is the fastest way to get going. Sign up and change your base-url. Statistics, visibility into the context window, and cost savings reports included.
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install virtual-context
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Python 3.11+, all core dependencies in the base install.
|
|
48
|
+
|
|
49
|
+
Optional storage backends: `pip install virtual-context[postgres]`, `[neo4j]`, or `[falkordb]`.
|
|
50
|
+
|
|
51
|
+
## Integration
|
|
52
|
+
|
|
53
|
+
virtual-context runs as a local HTTP proxy between your client and the upstream LLM API. Point your client at `localhost:5757` instead of the upstream. The proxy handles everything transparently: tagging, retrieval, history filtering, compaction, tool interception. Auto-detects Anthropic, OpenAI (Chat + Codex/Responses), and Gemini request formats.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
virtual-context proxy --upstream https://api.anthropic.com
|
|
57
|
+
virtual-context proxy --upstream https://api.openai.com
|
|
58
|
+
virtual-context proxy --upstream https://generativelanguage.googleapis.com
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
No config file needed for basic usage. For customization:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cp virtual-context.yaml.example virtual-context.yaml
|
|
65
|
+
virtual-context -c virtual-context.yaml proxy
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Claude Code
|
|
69
|
+
|
|
70
|
+
Set your base URL to point at the proxy:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# In your Claude Code config or environment
|
|
74
|
+
ANTHROPIC_BASE_URL=http://127.0.0.1:5757
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Claude Code's tool chains (file reads, searches, command output) are automatically compressed. A 937K-token payload with 52 tool chains collapses to ~65K. When Claude Code truncates history to manage its own context window, virtual-context detects the truncation and recovers stored context transparently.
|
|
78
|
+
|
|
79
|
+
### OpenClaw
|
|
80
|
+
|
|
81
|
+
Set these to allow OpenClaw to maintain large context windows from a client perspective:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
// 1. History limits (the real bottleneck most users will hit)
|
|
85
|
+
// channels.<provider> (e.g. channels.telegram)
|
|
86
|
+
"historyLimit": 99999,
|
|
87
|
+
"dmHistoryLimit": 99999
|
|
88
|
+
|
|
89
|
+
// global fallback
|
|
90
|
+
"messages": { "groupChat": { "historyLimit": 99999 } }
|
|
91
|
+
|
|
92
|
+
// 2. Model context window: must be on the provider in the per-agent models.json, with
|
|
93
|
+
// explicit model entries:
|
|
94
|
+
"anthropic": {
|
|
95
|
+
"baseUrl": "https://anthropic.virtual-context.com?vckey=...",
|
|
96
|
+
"api": "anthropic-messages",
|
|
97
|
+
"models": [
|
|
98
|
+
{
|
|
99
|
+
"id": "claude-opus-4-6",
|
|
100
|
+
"contextWindow": 2000000, // Note this is 2M
|
|
101
|
+
...
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Just setting baseUrl alone isn't enough. Without model entries, it falls back to pi-ai's
|
|
108
|
+
hardcoded 200K. And models.overrides in the global config is display only; it doesn't affect
|
|
109
|
+
actual windowing.
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
3. Context pruning: disable it so the proxy controls windowing:
|
|
113
|
+
"agents": {
|
|
114
|
+
"defaults": {
|
|
115
|
+
"contextPruning": { "mode": "off" },
|
|
116
|
+
"contextTokens": 2000000 // Note this is 2M
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
4. Session idle timeout: prevent OpenClaw from resetting sessions too early.
|
|
121
|
+
Without this, sessions reset after 12 hours by default, wiping the client-side
|
|
122
|
+
history before VC can manage it:
|
|
123
|
+
"session": {
|
|
124
|
+
"resetByType": {
|
|
125
|
+
"group": { "idleMinutes": 2880 } // 48 hours (default is 720 / 12h)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
A dedicated [OpenClaw plugin](https://github.com/openclaw/openclaw/pull/12082) is also in progress, using lifecycle hooks for sync retrieval (`message.pre`) and fire-and-forget compaction (`agent.post`).
|
|
131
|
+
|
|
132
|
+
### Other Clients (Cursor, Continue, any OpenAI-compatible client)
|
|
133
|
+
|
|
134
|
+
Any client that lets you set a base URL works. Point it at `http://127.0.0.1:5757` (Anthropic format) or `http://127.0.0.1:5757/v1` (OpenAI format):
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
# Python (anthropic SDK)
|
|
138
|
+
import anthropic
|
|
139
|
+
client = anthropic.Anthropic(base_url="http://127.0.0.1:5757")
|
|
140
|
+
|
|
141
|
+
# Python (openai SDK)
|
|
142
|
+
from openai import OpenAI
|
|
143
|
+
client = OpenAI(base_url="http://127.0.0.1:5757/v1")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Multi-instance mode** runs multiple providers on different ports in one process:
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
proxy:
|
|
150
|
+
instances:
|
|
151
|
+
- port: 5757
|
|
152
|
+
upstream: https://api.anthropic.com
|
|
153
|
+
label: anthropic
|
|
154
|
+
- port: 5758
|
|
155
|
+
upstream: https://api.openai.com
|
|
156
|
+
label: openai
|
|
157
|
+
- port: 5760
|
|
158
|
+
upstream: https://generativelanguage.googleapis.com
|
|
159
|
+
label: gemini
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Daemon mode** runs the proxy as a background service:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
virtual-context daemon install --upstream https://api.anthropic.com
|
|
166
|
+
virtual-context onboard --wizard --install-daemon
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Daemon lifecycle: `daemon status | start | stop | restart | uninstall`
|
|
170
|
+
|
|
171
|
+
Full setup docs (macOS `launchd`, Linux `systemd --user`, Windows Task Scheduler): [`docs/install.md`](docs/install.md)
|
|
172
|
+
|
|
173
|
+
### Python SDK
|
|
174
|
+
|
|
175
|
+
Two function calls wrap your existing LLM pipeline:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from virtual_context import VirtualContextEngine, Message
|
|
179
|
+
|
|
180
|
+
engine = VirtualContextEngine(config_path="./virtual-context.yaml")
|
|
181
|
+
|
|
182
|
+
# BEFORE sending to LLM: retrieve relevant stored context
|
|
183
|
+
assembled = engine.on_message_inbound(
|
|
184
|
+
message="What was the Henninger filing deadline?",
|
|
185
|
+
conversation_history=messages,
|
|
186
|
+
)
|
|
187
|
+
# assembled.prepend_text → enriched system prompt with retrieved summaries
|
|
188
|
+
# assembled.matched_tags → ["legal", "filing"]
|
|
189
|
+
|
|
190
|
+
# AFTER LLM responds: tag, index, compact if needed
|
|
191
|
+
report = engine.on_turn_complete(messages)
|
|
192
|
+
if report:
|
|
193
|
+
print(f"Compacted {report.segments_compacted} segments, freed {report.tokens_freed:,} tokens")
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### MCP Server
|
|
197
|
+
|
|
198
|
+
virtual-context also exposes an MCP server for Claude Desktop, Cursor, or any MCP-compatible client. The model calls tools like `recall_all`, `remember_when`, `find_quote`, `query_facts`, `expand_topic`, and `collapse_topic` internally to build robust memory. These are not user-facing commands; the model decides when to use them based on what the conversation needs.
|
|
199
|
+
|
|
200
|
+
## What It Does
|
|
201
|
+
|
|
202
|
+
### Automatic Topic Tagging
|
|
203
|
+
|
|
204
|
+
There are no predefined domains to configure. An LLM tagger reads each turn and generates semantic tags (`database`, `auth`, `fitness`, `legal`) that naturally converge over the session. A vocabulary feedback loop passes known tags back into the tagger prompt, so it reuses `storage` instead of inventing `data-persistence` or `file-management`. When synonyms slip through (`db` vs `database`), a canonicalizer detects aliases via edit distance and normalizes them automatically.
|
|
205
|
+
|
|
206
|
+
When a tag appears on too many turns and loses discriminative power, virtual-context detects this and automatically splits it into narrower sub-tags. In a 143-turn OpenClaw session, `reservation-request` (43 turns, 30%) was split into `reservation-platform-troubleshooting`, `reservation-availability-search`, `reservation-browser-access`, and `reservation-general`. The vocabulary evolves toward maximum precision without manual curation.
|
|
207
|
+
|
|
208
|
+
### Structured Fact Extraction
|
|
209
|
+
|
|
210
|
+
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.
|
|
211
|
+
|
|
212
|
+
virtual-context extracts structured facts during compaction: subject, verb, object, fact type (`preference`, `biographical`, `decision`, `plan`, `routine`, `medical`, `financial`), temporal status (`active`, `completed`, `planned`, `abandoned`, `recurring`), session provenance, and source turn numbers. Facts are queryable by any combination of these fields.
|
|
213
|
+
|
|
214
|
+
When new information contradicts a stored fact ("I moved from NYC to LA"), the supersession checker detects the conflict and marks the old fact as superseded. Facts have typed relationships (`SUPERSEDES`, `CAUSED_BY`, `PART_OF`, `CONTRADICTS`, `SAME_AS`, `RELATED_TO`) that are automatically detected and traversed during queries.
|
|
215
|
+
|
|
216
|
+
### Tool Chain Compression
|
|
217
|
+
|
|
218
|
+
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.
|
|
219
|
+
|
|
220
|
+
virtual-context collapses entire tool chains into compact stubs:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
Before (3 messages, ~18K tokens):
|
|
224
|
+
assistant: [tool_use: Read file.py]
|
|
225
|
+
user: [tool_result: <full 500-line file contents>]
|
|
226
|
+
assistant: "The file has a bug on line 42..."
|
|
227
|
+
|
|
228
|
+
After (2 messages, ~200 tokens):
|
|
229
|
+
user: [compacted turn: Read(file.py)]
|
|
230
|
+
assistant: "The file has a bug on line 42..."
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Handles all four provider formats (Anthropic, OpenAI Chat, OpenAI Responses, Gemini). Full raw tool output is stored durably and recoverable on demand. Past a configurable age threshold, stubs are dropped entirely (the segment summaries already cover that content).
|
|
234
|
+
|
|
235
|
+
### Media Compression
|
|
236
|
+
|
|
237
|
+
Base64 images in API payloads are enormous: a single screenshot is 300-500KB of base64, consuming ~100K tokens. virtual-context compresses images on first sight: a 391KB screenshot becomes ~40KB, saving ~88K tokens per image. Originals are stored to disk for recovery. This runs on both passthrough and active paths, so even conversations that haven't triggered compaction benefit.
|
|
238
|
+
|
|
239
|
+
### Virtual Memory Paging
|
|
240
|
+
|
|
241
|
+
RAG retrieves content and appends it to the context window. It never frees space from what's already there. virtual-context treats the context window as managed memory with bidirectional paging:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
Tag summaries <-------> Segment summaries <-------> Full stored text
|
|
245
|
+
^ ^ ^
|
|
246
|
+
collapse default expand
|
|
247
|
+
(~200t) (~2,000t) (~8,000t+)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
When the model needs more detail on a topic, it expands that topic from summary to full stored text. When budget pressure hits, cold topics are automatically collapsed. The working set persists across turns, so expansion decisions are stateful.
|
|
251
|
+
|
|
252
|
+
### Cross-Vocabulary Retrieval
|
|
253
|
+
|
|
254
|
+
Users don't use the same words every time. "Materialized views for feed performance" at turn 46 might be recalled as "that caching trick for the feed" at turn 71. Pure tag overlap finds nothing.
|
|
255
|
+
|
|
256
|
+
virtual-context uses 3-signal retrieval scoring via Reciprocal Rank Fusion: IDF-weighted tag overlap, BM25 keyword search on summaries, and embedding cosine similarity. Related tags generated at both write time and query time bridge vocabulary gaps. When tag-based retrieval misses entirely, full-text and semantic search across stored conversation text provide a fallback.
|
|
257
|
+
|
|
258
|
+
### Time-Scoped Recall
|
|
259
|
+
|
|
260
|
+
Queries like "going back to the very beginning, what were the key decisions?" or "between June and July, what changed?" reference a position in time, not just a topic. virtual-context combines semantic query matching with structured time ranges. Date math is backend-resolved, not LLM-resolved, so results are deterministic. Session dates propagate through the entire pipeline: every segment knows when it happened, and temporal ordering is always accurate.
|
|
261
|
+
|
|
262
|
+
### Configurable Context Ceiling
|
|
263
|
+
|
|
264
|
+
Most teams set `context_window` to whatever the model supports and let it fill up. This is expensive and degrades quality. Research on "lost in the middle" shows that LLM attention degrades in long contexts: facts buried in 200K tokens of raw history are missed more often than the same facts concentrated in a managed window.
|
|
265
|
+
|
|
266
|
+
```yaml
|
|
267
|
+
context_window: 60000 # run a 200K model at 60K
|
|
268
|
+
compaction:
|
|
269
|
+
soft_threshold: 0.70
|
|
270
|
+
hard_threshold: 0.90
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
A 200K-capable model running at 60K uses ~70% fewer input tokens per request. The model's attention is concentrated on curated, high-signal context rather than spread across mostly-stale history.
|
|
274
|
+
|
|
275
|
+
### Store-Backed Recovery
|
|
276
|
+
|
|
277
|
+
Clients (Claude Code, OpenClaw) sometimes truncate conversation history to manage their own context windows. virtual-context detects the truncation and recovers from its durable store: chain snapshots, recent raw turns, sanitized and restored transparently. The payload that reaches the LLM contains the recovered context as if it had never been truncated.
|
|
278
|
+
|
|
279
|
+
## VCATTACH: Shared Memory Across Platforms
|
|
280
|
+
|
|
281
|
+
Every conversation gets a stable identity derived from the system prompt hash and conversation markers embedded in assistant responses. This identity persists across restarts, deploys, and client changes.
|
|
282
|
+
|
|
283
|
+
When identity detaches (system prompt changes, client truncation loses the marker, a deploy produces a different hash), type:
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
VCATTACH <conversation_id>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The target can be a conversation label (`VCATTACH website`), a full UUID, or a UUID prefix.
|
|
290
|
+
|
|
291
|
+
**Reattachment after detachment.** A deploy or system prompt change creates an orphan conversation. `VCATTACH <label>` reconnects to the original conversation with all segments, facts, and tags intact.
|
|
292
|
+
|
|
293
|
+
**Cross-platform shared memory.** Build up deep context in Claude Code (architecture decisions, code patterns, debugging history), then type `VCATTACH code-project` in a Telegram conversation with a different model. 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.
|
|
294
|
+
|
|
295
|
+
**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.
|
|
296
|
+
|
|
297
|
+
**Conversation merging.** Two conversations about the same topic? Pick the one with richer context and `VCATTACH` the other to it. The old conversation is deleted; the target keeps all its compacted data.
|
|
298
|
+
|
|
299
|
+
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.
|
|
300
|
+
|
|
301
|
+
## Virtual-Context vs RAG vs Compaction
|
|
302
|
+
|
|
303
|
+
These approaches are complementary. RAG, other memory systems, and compaction can all run alongside virtual-context.
|
|
304
|
+
|
|
305
|
+
| | RAG | Compaction-only | virtual-context |
|
|
306
|
+
|---|---|---|---|
|
|
307
|
+
| **Primary mechanism** | Query-time retrieval by embedding similarity | Summarize old history to fit window | Tagged memory + retrieval + compaction + paging tools |
|
|
308
|
+
| **What gets kept** | External documents + recent raw chat | Summaries of old turns + recent raw chat | Multi-layer memory (raw turns, segment summaries, tag summaries) |
|
|
309
|
+
| **Specific fact lookup** | Depends on embedding/query phrasing alignment | Lossy after summarization | Structured fact queries + full-text search + summary drill-down |
|
|
310
|
+
| **Broad overview** | Weak unless special orchestration | Can summarize, but often generic | All topic summaries loaded within budget |
|
|
311
|
+
| **Time-scoped recall** | Custom logic outside core RAG | Requires date fidelity in summaries | Backend-resolved time ranges with session date propagation |
|
|
312
|
+
| **Vocabulary mismatch tolerance** | Embedding-dependent | Low | 3-signal RRF fusion + related-tag expansion + semantic search fallback |
|
|
313
|
+
| **Context budget control** | Append retrieved chunks | Compression with limited rehydration | Explicit paging: expand/collapse topics with bounded assembly |
|
|
314
|
+
| **Cost at scale** | Grows with corpus size | Grows with conversation length | Configurable ceiling: run a 200K model at 30K |
|
|
315
|
+
| **Best fit** | Knowledge/doc retrieval | Simple long-chat cost reduction | Long-running agent memory with mixed query types |
|
|
316
|
+
|
|
317
|
+
## Proxy Features
|
|
318
|
+
|
|
319
|
+
The proxy includes a [live dashboard](#live-dashboard) at `http://localhost:5757/dashboard` with request grid, turn inspector, session stats, telemetry, and SSE live updates.
|
|
320
|
+
|
|
321
|
+
- **Conversation continuity** via invisible markers in assistant responses, with stable identity derived from system prompt hash
|
|
322
|
+
- **Redis session cache** for lossless restarts across container deploys (falls back gracefully if Redis is unavailable)
|
|
323
|
+
- **Four-format support** auto-detected per request (Anthropic, OpenAI Chat, OpenAI Responses, Gemini)
|
|
324
|
+
- **History ingestion** bootstraps the tag index from existing conversation on the first request
|
|
325
|
+
- **Streaming with zero added latency** (SSE forwarded byte-for-byte, text accumulated in background)
|
|
326
|
+
- **Error-resilient** (engine failures fall back to unmodified passthrough; bloat fallback reverts to original payload)
|
|
327
|
+
- **Envelope stripping** extracts sender identity and timestamps from metadata blocks (group chat participants appear as real names)
|
|
328
|
+
- **Image-aware token counting** using Anthropic formula, not raw base64 tokenization
|
|
329
|
+
- **Per-port config** for multi-instance setups with isolated engines and storage
|
|
330
|
+
- **Telemetry** on every LLM call: token counts, cost, timing across five components (`compactor`, `tagger`, `tool_loop`, `fact_curator`, `proxy_upstream`)
|
|
331
|
+
|
|
332
|
+
## CLI
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
virtual-context proxy -u https://api.anthropic.com # start proxy
|
|
336
|
+
virtual-context status # tag stats and token usage
|
|
337
|
+
virtual-context tags # list all tags
|
|
338
|
+
virtual-context domains # tags with turn counts and summaries
|
|
339
|
+
virtual-context recall auth # retrieve stored summaries for a tag
|
|
340
|
+
virtual-context retrieve -m "What about auth?" # tag + retrieve (JSON)
|
|
341
|
+
virtual-context transform -m "What about auth?" # tag + retrieve + assemble
|
|
342
|
+
virtual-context compact -i msgs.json # manual compaction
|
|
343
|
+
virtual-context aliases list|suggest|add # tag alias management
|
|
344
|
+
virtual-context init coding # create config from preset
|
|
345
|
+
virtual-context onboard [--wizard] # guided setup
|
|
346
|
+
virtual-context daemon install|status|start|stop # background service
|
|
347
|
+
virtual-context config validate # check config syntax
|
|
348
|
+
virtual-context telemetry [--verbose] [--json] # cost, tokens, timing
|
|
349
|
+
virtual-context chat [--headless] [--replay ...] # interactive TUI or headless
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Interactive Chat (TUI)
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
virtual-context chat --config virtual-context.yaml
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Terminal chat interface with live context visualization: tag panel with activity levels, real-time budget bar, turn inspector (Ctrl+I), manual compaction (`/compact` or Ctrl+K), session export (Ctrl+S). Headless mode (`--headless --replay prompts.txt`) for automated testing and regression validation.
|
|
359
|
+
|
|
360
|
+
## Stress-Tested
|
|
361
|
+
|
|
362
|
+
Validated against adversarial 100-turn conversations with deliberately overlapping domains, vocabulary mismatches, ambiguous callbacks, and cross-domain synthesis queries, using a 3,000-token context window with Claude Haiku. 89% pass rate on 28 deliberately adversarial prompts. Tag vocabulary stabilizes within 10-15 turns via the feedback loop.
|
|
363
|
+
|
|
364
|
+
Also validated in production with OpenClaw (Telegram) handling real multi-topic conversations: tool chain preservation across 90-message conversations (52 messages filtered to 27 without breaking a single tool dependency), live embedding matching against 40+ tag vocabularies, and single-pass history ingestion of 43 pre-existing turns.
|
|
365
|
+
|
|
366
|
+
## Benchmark Results
|
|
367
|
+
|
|
368
|
+
### LongMemEval (100 Questions)
|
|
369
|
+
|
|
370
|
+
100 random questions from [LongMemEval-500](https://github.com/xiaowu0162/LongMemEval) (5 batches x 20, seeds 42/99/777/1234/2025).
|
|
371
|
+
|
|
372
|
+
**Configuration:**
|
|
373
|
+
- **VC:** MiMo-V2-Flash (ingestion) + Claude Sonnet 4.5 (reader) + Gemini 3 Pro Preview (judge)
|
|
374
|
+
- **Baseline:** Claude Sonnet 4.5 with full conversation history (~118K tokens) + Gemini 3 Pro Preview (judge)
|
|
375
|
+
|
|
376
|
+
| Metric | VC | Baseline |
|
|
377
|
+
|--------|-----|----------|
|
|
378
|
+
| Accuracy | 95/100 (95%) | 33/100 (33%) |
|
|
379
|
+
| Avg Tokens/Question | 52,347 | 117,582 |
|
|
380
|
+
| Avg Cost/Question | $0.16 | $0.36 |
|
|
381
|
+
| Total Cost | $15.99 | $35.56 |
|
|
382
|
+
| Token Reduction | 2.2x fewer | -- |
|
|
383
|
+
|
|
384
|
+
#### Accuracy by Question Type
|
|
385
|
+
|
|
386
|
+
| Category | Count | VC | Baseline |
|
|
387
|
+
|----------|-------|----|----------|
|
|
388
|
+
| knowledge-update | 17 | 100.0% (17/17) | 29.4% (5/17) |
|
|
389
|
+
| multi-session | 26 | 88.5% (23/26) | 15.4% (4/26) |
|
|
390
|
+
| temporal-reasoning | 28 | 92.9% (26/28) | 32.1% (9/28) |
|
|
391
|
+
| single-session-user | 13 | 100.0% (13/13) | 46.2% (6/13) |
|
|
392
|
+
| single-session-assistant | 11 | 100.0% (11/11) | 72.7% (8/11) |
|
|
393
|
+
| single-session-preference | 5 | 100.0% (5/5) | 20.0% (1/5) |
|
|
394
|
+
|
|
395
|
+
<details>
|
|
396
|
+
<summary>Click to expand full results table (100 questions)</summary>
|
|
397
|
+
|
|
398
|
+
| ID | Type | BL | BL Tokens | BL Cost | VC | VC Tokens | VC Cost |
|
|
399
|
+
|----|------|-----|-----------|---------|-----|-----------|---------|
|
|
400
|
+
| `07741c44` | knowledge-update | FAIL | 116,404 | $0.35 | pass | 49,721 | $0.15 |
|
|
401
|
+
| `0977f2af` | knowledge-update | FAIL | 117,359 | $0.35 | pass | 49,734 | $0.15 |
|
|
402
|
+
| `0ddfec37` | knowledge-update | FAIL | 115,848 | $0.35 | pass | 43,780 | $0.13 |
|
|
403
|
+
| `2133c1b5_abs` | knowledge-update | pass | 116,186 | $0.36 | pass | 56,533 | $0.17 |
|
|
404
|
+
| `2698e78f_abs` | knowledge-update | FAIL | 118,841 | $0.36 | pass | 36,039 | $0.11 |
|
|
405
|
+
| `3ba21379` | knowledge-update | FAIL | 116,604 | $0.35 | pass | 46,034 | $0.14 |
|
|
406
|
+
| `4b24c848` | knowledge-update | pass | 117,107 | $0.35 | pass | 32,494 | $0.10 |
|
|
407
|
+
| `4d6b87c8` | knowledge-update | FAIL | 115,104 | $0.35 | pass | 47,262 | $0.14 |
|
|
408
|
+
| `50635ada` | knowledge-update | FAIL | 118,682 | $0.36 | pass | 41,677 | $0.13 |
|
|
409
|
+
| `5a4f22c0` | knowledge-update | pass | 118,775 | $0.36 | pass | 35,437 | $0.11 |
|
|
410
|
+
| `6071bd76` | knowledge-update | FAIL | 117,904 | $0.36 | pass | 36,618 | $0.11 |
|
|
411
|
+
| `6aeb4375` | knowledge-update | pass | 115,001 | $0.35 | pass | 38,984 | $0.12 |
|
|
412
|
+
| `89941a94` | knowledge-update | FAIL | 117,038 | $0.35 | pass | 45,347 | $0.14 |
|
|
413
|
+
| `8fb83627` | knowledge-update | pass | 115,488 | $0.35 | pass | 35,041 | $0.11 |
|
|
414
|
+
| `a1eacc2a` | knowledge-update | FAIL | 117,513 | $0.35 | pass | 46,401 | $0.14 |
|
|
415
|
+
| `cf22b7bf` | knowledge-update | FAIL | 115,784 | $0.35 | pass | 49,002 | $0.15 |
|
|
416
|
+
| `ed4ddc30` | knowledge-update | FAIL | 118,045 | $0.36 | pass | 37,708 | $0.11 |
|
|
417
|
+
| `099778bb` | multi-session | FAIL | 118,622 | $0.36 | pass | 33,375 | $0.10 |
|
|
418
|
+
| `09ba9854` | multi-session | FAIL | 115,128 | $0.35 | FAIL | 36,120 | $0.11 |
|
|
419
|
+
| `0ea62687` | multi-session | FAIL | 116,840 | $0.36 | pass | 36,910 | $0.11 |
|
|
420
|
+
| `21d02d0d` | multi-session | FAIL | 119,667 | $0.36 | pass | 44,069 | $0.13 |
|
|
421
|
+
| `36b9f61e` | multi-session | FAIL | 116,713 | $0.35 | pass | 42,919 | $0.13 |
|
|
422
|
+
| `3fe836c9` | multi-session | FAIL | 117,954 | $0.35 | pass | 45,463 | $0.14 |
|
|
423
|
+
| `46a3abf7` | multi-session | FAIL | 117,783 | $0.35 | pass | 132,933 | $0.40 |
|
|
424
|
+
| `6456829e_abs` | multi-session | FAIL | 117,467 | $0.35 | pass | 42,898 | $0.13 |
|
|
425
|
+
| `681a1674` | multi-session | FAIL | 118,545 | $0.36 | pass | 62,141 | $0.19 |
|
|
426
|
+
| `720133ac` | multi-session | FAIL | 120,053 | $0.37 | pass | 50,205 | $0.15 |
|
|
427
|
+
| `7405e8b1` | multi-session | FAIL | 118,694 | $0.36 | pass | 50,989 | $0.16 |
|
|
428
|
+
| `88432d0a` | multi-session | FAIL | 118,401 | $0.36 | pass | 46,391 | $0.14 |
|
|
429
|
+
| `88432d0a_abs` | multi-session | pass | 119,275 | $0.36 | pass | 55,463 | $0.17 |
|
|
430
|
+
| `9d25d4e0` | multi-session | FAIL | 117,978 | $0.36 | pass | 83,295 | $0.25 |
|
|
431
|
+
| `a11281a2` | multi-session | FAIL | 119,807 | $0.36 | pass | 49,939 | $0.15 |
|
|
432
|
+
| `a346bb18` | multi-session | FAIL | 118,452 | $0.36 | pass | 44,404 | $0.14 |
|
|
433
|
+
| `a96c20ee` | multi-session | FAIL | 117,282 | $0.35 | pass | 42,068 | $0.13 |
|
|
434
|
+
| `bf659f65` | multi-session | FAIL | 114,781 | $0.35 | FAIL | 41,952 | $0.13 |
|
|
435
|
+
| `d682f1a2` | multi-session | FAIL | 117,856 | $0.35 | pass | 48,821 | $0.15 |
|
|
436
|
+
| `dd2973ad` | multi-session | pass | 117,351 | $0.36 | pass | 56,463 | $0.17 |
|
|
437
|
+
| `e56a43b9` | multi-session | pass | 119,177 | $0.36 | pass | 47,528 | $0.14 |
|
|
438
|
+
| `e6041065` | multi-session | FAIL | 117,316 | $0.35 | pass | 38,473 | $0.12 |
|
|
439
|
+
| `eeda8a6d` | multi-session | FAIL | 118,197 | $0.36 | pass | 45,726 | $0.14 |
|
|
440
|
+
| `ef66a6e5` | multi-session | FAIL | 116,328 | $0.35 | pass | 152,680 | $0.46 |
|
|
441
|
+
| `gpt4_372c3eed` | multi-session | pass | 117,552 | $0.36 | FAIL | 46,299 | $0.14 |
|
|
442
|
+
| `gpt4_d84a3211` | multi-session | FAIL | 116,459 | $0.35 | pass | 51,487 | $0.16 |
|
|
443
|
+
| `0db4c65d` | temporal-reasoning | FAIL | 115,780 | $0.35 | pass | 45,639 | $0.14 |
|
|
444
|
+
| `2ebe6c90` | temporal-reasoning | FAIL | 115,113 | $0.35 | pass | 39,883 | $0.12 |
|
|
445
|
+
| `6613b389` | temporal-reasoning | pass | 119,268 | $0.37 | pass | 41,228 | $0.13 |
|
|
446
|
+
| `a3045048` | temporal-reasoning | FAIL | 116,689 | $0.35 | pass | 47,120 | $0.14 |
|
|
447
|
+
| `b29f3365` | temporal-reasoning | FAIL | 118,078 | $0.36 | pass | 43,563 | $0.13 |
|
|
448
|
+
| `c8090214_abs` | temporal-reasoning | pass | 116,460 | $0.35 | pass | 79,046 | $0.24 |
|
|
449
|
+
| `cc6d1ec1` | temporal-reasoning | pass | 116,218 | $0.35 | pass | 47,747 | $0.15 |
|
|
450
|
+
| `eac54adc` | temporal-reasoning | FAIL | 119,492 | $0.36 | pass | 40,470 | $0.12 |
|
|
451
|
+
| `f0853d11` | temporal-reasoning | pass | 116,117 | $0.35 | pass | 46,903 | $0.14 |
|
|
452
|
+
| `gpt4_18c2b244` | temporal-reasoning | FAIL | 119,183 | $0.36 | pass | 53,922 | $0.17 |
|
|
453
|
+
| `gpt4_1a1dc16d` | temporal-reasoning | FAIL | 120,646 | $0.37 | pass | 52,119 | $0.16 |
|
|
454
|
+
| `gpt4_1e4a8aec` | temporal-reasoning | pass | 118,208 | $0.36 | pass | 48,286 | $0.15 |
|
|
455
|
+
| `gpt4_21adecb5` | temporal-reasoning | FAIL | 119,249 | $0.36 | pass | 125,864 | $0.38 |
|
|
456
|
+
| `gpt4_483dd43c` | temporal-reasoning | FAIL | 117,942 | $0.35 | pass | 43,327 | $0.13 |
|
|
457
|
+
| `gpt4_4929293b` | temporal-reasoning | FAIL | 118,774 | $0.37 | pass | 58,869 | $0.18 |
|
|
458
|
+
| `gpt4_4cd9eba1` | temporal-reasoning | pass | 119,611 | $0.36 | pass | 46,083 | $0.14 |
|
|
459
|
+
| `gpt4_5438fa52` | temporal-reasoning | FAIL | 114,753 | $0.35 | pass | 51,194 | $0.16 |
|
|
460
|
+
| `gpt4_65aabe59` | temporal-reasoning | FAIL | 115,392 | $0.35 | pass | 39,931 | $0.12 |
|
|
461
|
+
| `gpt4_70e84552` | temporal-reasoning | FAIL | 117,453 | $0.35 | pass | 42,109 | $0.13 |
|
|
462
|
+
| `gpt4_7ca326fa` | temporal-reasoning | FAIL | 116,432 | $0.35 | pass | 51,589 | $0.16 |
|
|
463
|
+
| `gpt4_7de946e7` | temporal-reasoning | pass | 117,096 | $0.35 | pass | 44,183 | $0.14 |
|
|
464
|
+
| `gpt4_8279ba02` | temporal-reasoning | FAIL | 115,780 | $0.35 | pass | 156,923 | $0.47 |
|
|
465
|
+
| `gpt4_88806d6e` | temporal-reasoning | FAIL | 119,052 | $0.36 | pass | 33,463 | $0.10 |
|
|
466
|
+
| `gpt4_98f46fc6` | temporal-reasoning | pass | 117,366 | $0.36 | pass | 58,524 | $0.18 |
|
|
467
|
+
| `gpt4_d6585ce9` | temporal-reasoning | FAIL | 115,862 | $0.35 | pass | 50,320 | $0.15 |
|
|
468
|
+
| `gpt4_d9af6064` | temporal-reasoning | pass | 116,298 | $0.35 | pass | 48,037 | $0.15 |
|
|
469
|
+
| `gpt4_f420262c` | temporal-reasoning | FAIL | 116,610 | $0.35 | FAIL | 134,691 | $0.41 |
|
|
470
|
+
| `gpt4_f420262d` | temporal-reasoning | FAIL | 118,803 | $0.36 | FAIL | 52,815 | $0.16 |
|
|
471
|
+
| `001be529` | ss-user | FAIL | 117,394 | $0.35 | pass | 40,375 | $0.12 |
|
|
472
|
+
| `15745da0` | ss-user | FAIL | 120,384 | $0.37 | pass | 53,318 | $0.16 |
|
|
473
|
+
| `19b5f2b3` | ss-user | pass | 115,688 | $0.35 | pass | 42,046 | $0.13 |
|
|
474
|
+
| `19b5f2b3_abs` | ss-user | pass | 116,214 | $0.35 | pass | 44,256 | $0.14 |
|
|
475
|
+
| `37d43f65` | ss-user | FAIL | 117,911 | $0.35 | pass | 72,955 | $0.22 |
|
|
476
|
+
| `4fd1909e` | ss-user | FAIL | 119,200 | $0.36 | pass | 50,759 | $0.15 |
|
|
477
|
+
| `577d4d32` | ss-user | pass | 116,583 | $0.35 | pass | 48,225 | $0.15 |
|
|
478
|
+
| `60d45044` | ss-user | FAIL | 119,224 | $0.36 | pass | 47,125 | $0.14 |
|
|
479
|
+
| `853b0a1d` | ss-user | FAIL | 116,684 | $0.35 | pass | 48,110 | $0.15 |
|
|
480
|
+
| `8e9d538c` | ss-user | pass | 118,317 | $0.36 | pass | 42,345 | $0.13 |
|
|
481
|
+
| `ad7109d1` | ss-user | FAIL | 114,263 | $0.34 | pass | 49,802 | $0.15 |
|
|
482
|
+
| `af8d2e46` | ss-user | pass | 114,690 | $0.35 | pass | 53,504 | $0.16 |
|
|
483
|
+
| `f4f1d8a4_abs` | ss-user | pass | 118,760 | $0.36 | pass | 46,426 | $0.14 |
|
|
484
|
+
| `0e5e2d1a` | ss-assistant | pass | 118,067 | $0.35 | pass | 45,569 | $0.14 |
|
|
485
|
+
| `1de5cff2` | ss-assistant | FAIL | 118,432 | $0.36 | pass | 45,809 | $0.14 |
|
|
486
|
+
| `28bcfaac` | ss-assistant | pass | 118,509 | $0.36 | pass | 44,713 | $0.14 |
|
|
487
|
+
| `41275add` | ss-assistant | FAIL | 118,490 | $0.36 | pass | 51,010 | $0.16 |
|
|
488
|
+
| `58470ed2` | ss-assistant | pass | 118,116 | $0.36 | pass | 80,240 | $0.25 |
|
|
489
|
+
| `6222b6eb` | ss-assistant | pass | 118,378 | $0.36 | pass | 41,408 | $0.13 |
|
|
490
|
+
| `8aef76bc` | ss-assistant | pass | 118,739 | $0.36 | pass | 32,131 | $0.10 |
|
|
491
|
+
| `ceb54acb` | ss-assistant | pass | 118,463 | $0.37 | pass | 45,166 | $0.14 |
|
|
492
|
+
| `dc439ea3` | ss-assistant | pass | 118,782 | $0.36 | pass | 57,967 | $0.18 |
|
|
493
|
+
| `e3fc4d6e` | ss-assistant | FAIL | 115,974 | $0.35 | pass | 51,285 | $0.16 |
|
|
494
|
+
| `f523d9fe` | ss-assistant | pass | 119,321 | $0.36 | pass | 58,638 | $0.18 |
|
|
495
|
+
| `1a1907b4` | ss-preference | FAIL | 117,865 | $0.35 | pass | 51,663 | $0.16 |
|
|
496
|
+
| `1da05512` | ss-preference | FAIL | 120,425 | $0.37 | pass | 54,796 | $0.17 |
|
|
497
|
+
| `b0479f84` | ss-preference | FAIL | 117,425 | $0.36 | pass | 48,987 | $0.15 |
|
|
498
|
+
| `b6025781` | ss-preference | FAIL | 119,376 | $0.36 | pass | 46,189 | $0.14 |
|
|
499
|
+
| `fca70973` | ss-preference | pass | 117,421 | $0.36 | pass | 59,228 | $0.19 |
|
|
500
|
+
| **Total** | **100** | **33** | **11,758,181** | **$35.56** | **95** | **5,234,716** | **$15.99** |
|
|
501
|
+
|
|
502
|
+
</details>
|
|
503
|
+
|
|
504
|
+
## Development
|
|
505
|
+
|
|
506
|
+
```bash
|
|
507
|
+
git clone https://github.com/virtual-context/virtual-context.git
|
|
508
|
+
cd virtual-context
|
|
509
|
+
python -m venv .venv && source .venv/bin/activate
|
|
510
|
+
pip install -e ".[dev]"
|
|
511
|
+
python -m pytest tests/ -v --ignore=tests/ollama # ~1500 unit tests
|
|
512
|
+
python -m pytest tests/ollama/ -v -m ollama # integration (requires Ollama)
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
## License
|
|
516
|
+
|
|
517
|
+
AGPL-3.0, Copyright Y. Ahmed Kidwai
|
|
518
|
+
|
|
519
|
+
For commercial licensing inquiries, contact: ahmed@kidw.ai
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
<!-- [](https://pypi.org/project/virtual-context/) -->
|
|
2
|
+
<!-- [](https://pypi.org/project/virtual-context/) -->
|
|
3
|
+
<!-- [](https://pypistats.org/packages/virtual-context) -->
|
|
4
|
+
<!-- [](https://github.com/yursilkidwai/virtual-context/blob/main/LICENSE) -->
|
|
5
|
+
[](https://discord.gg/YxDHKEZz)
|
|
6
|
+
[](https://x.com/virtualctx)
|
|
7
|
+
|
|
1
8
|
<p align="center">
|
|
2
9
|
<a href="assets/dashboard.png">
|
|
3
10
|
<img src="assets/dashboard.png" alt="virtual-context dashboard" width="800">
|
|
@@ -538,6 +545,44 @@ Clients (Claude Code, OpenClaw) sometimes truncate conversation history to manag
|
|
|
538
545
|
|
|
539
546
|
Recovery is transparent to the client. The payload that reaches the LLM contains the recovered context as if it had never been truncated.
|
|
540
547
|
|
|
548
|
+
### Conversation Identity and VCATTACH
|
|
549
|
+
|
|
550
|
+
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.
|
|
551
|
+
|
|
552
|
+
When identity detaches — system prompt changes, client truncation loses the marker, a deploy produces a different hash — the user can reattach by typing:
|
|
553
|
+
|
|
554
|
+
```
|
|
555
|
+
VCATTACH <conversation_id>
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
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.
|
|
559
|
+
|
|
560
|
+
**What VCATTACH enables:**
|
|
561
|
+
|
|
562
|
+
**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.
|
|
563
|
+
|
|
564
|
+
**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.
|
|
565
|
+
|
|
566
|
+
**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.
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
**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.
|
|
570
|
+
|
|
571
|
+
**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.
|
|
572
|
+
|
|
573
|
+
**How it works:**
|
|
574
|
+
|
|
575
|
+
1. User types `VCATTACH <label_or_id>` as a normal message
|
|
576
|
+
2. Proxy detects the command (regex on the last user message only — history is inert)
|
|
577
|
+
3. Resolves target by label (case-insensitive) or UUID (exact or prefix match)
|
|
578
|
+
4. Registers an alias so old markers redirect permanently
|
|
579
|
+
5. Deletes the old conversation's engine state
|
|
580
|
+
6. Resets the target's compaction checkpoints (segments/facts preserved)
|
|
581
|
+
7. Returns a fake response with the target's conversation marker — no LLM call
|
|
582
|
+
8. Next request carries the new marker and routes to the target
|
|
583
|
+
|
|
584
|
+
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.
|
|
585
|
+
|
|
541
586
|
### Virtual Memory Paging
|
|
542
587
|
|
|
543
588
|
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.
|