virtual-context 0.2.2__tar.gz → 0.2.3__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.2.2 → virtual_context-0.2.3}/PKG-INFO +1 -1
- {virtual_context-0.2.2 → virtual_context-0.2.3}/pyproject.toml +1 -1
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/__init__.py +1 -1
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/hint_builder.py +6 -2
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/handlers.py +100 -12
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/message_filter.py +96 -2
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/server.py +11 -1
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/types.py +1 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/.gitignore +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/LICENSE +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/README.md +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/assets/hero.png +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/models.yaml +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/REGRESSION_MAP.md +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/conftest.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/docker-compose.test.yml +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/haiku/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/haiku/conftest.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/haiku/test_compaction.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/haiku/test_retrieval.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/haiku/test_tagging.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/ollama/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/ollama/conftest.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/ollama/test_compactor.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/ollama/test_pipeline.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/ollama/test_provider.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/ollama/test_tag_generator.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/proxy/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/proxy/test_dashboard_cors.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/proxy/test_metrics.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_assembler.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_backend_integration.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_cli_init.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_compaction_commit_prune.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_compactor.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_compactor_concurrent.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_composite_store.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_config.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_context_bleed.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_conversation_identity.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_conversation_lifecycle.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_conversation_scoping.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_embedding_tag_generator.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_empty_turn_skip.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_engine_integration.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_engine_lookback.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_engine_state.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_fact_enrichment.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_fact_graph_integration.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_fact_link_checker.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_fact_link_query.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_fact_link_types.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_fact_links_sqlite.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_find_quote.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_headless.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_history_filter.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_idf_retrieval.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_ingest_index_integrity.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_longmemeval_auth.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_mcp_server.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_message_filter.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_metrics_persistence.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_model_catalog.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_model_limits.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_monitor.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_multi_instance.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_noop_fact_link_store.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_openrouter_provider.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_paging.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_passthrough_filter.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_presets.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_prev_context_leak.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_provider_adapters.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_proxy.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_proxy_dashboard.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_proxy_formats.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_proxy_message_filter.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_proxy_session.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_proxy_streaming.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_raw_content.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_recall_all.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_request_captures_persistence.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_retriever.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_rrf_scoring.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_segmenter.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_semantic_search.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_sender_identity.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_session_cache.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_session_date.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_storage_protocols.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_store_sqlite.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_stub_turn_handling.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_supersession.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_supersession_migration.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_tag_canonicalizer.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_tag_consolidator.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_tag_generator.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_tag_splitter.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_telemetry.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_telemetry_integration.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_tool_loop.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_tool_output_interceptor.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_tool_result_filter.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_tool_tags.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_tui.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_turn_tag_index.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_unified_budget.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_upstream_trim.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/tests/test_verb_expansion.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual-context.yaml.example +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/cli/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/cli/main.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/config.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/conversation_identity.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/assembler.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/compaction_pipeline.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/compactor.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/composite_store.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/conversation_store.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/embedding_provider.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/embedding_tag_generator.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/engine_utils.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/fact_query.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/fts_preprocessor.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/llm_utils.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/math_utils.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/model_catalog.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/monitor.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/paging_manager.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/protocols.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/provider_adapters.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/quote_search.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/retrieval_assembler.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/retrieval_scoring.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/retriever.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/search_engine.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/segmenter.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/semantic_search.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/store.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/tag_canonicalizer.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/tag_consolidator.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/tag_generator.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/tag_scoring.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/tag_splitter.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/tagging_pipeline.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/telemetry.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/temporal_resolver.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/tool_loop.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/tool_query.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/core/turn_tag_index.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/data/anthropic-tokenizer/tokenizer.json +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/engine.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/ingest/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/ingest/curator.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/ingest/date_resolver.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/ingest/parsers.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/ingest/supersession.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/mcp/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/mcp/server.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/model_limits.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/openclaw/virtual-context.mjs +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/patterns.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/presets/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/presets/agentic.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/presets/base.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/presets/coding.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/providers/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/providers/anthropic.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/providers/base.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/providers/generic_openai.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/providers/ollama_native.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/_envelope.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/dashboard.html +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/dashboard.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/formats.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/helpers.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/metrics.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/multi.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/registry.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/session_cache.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/state.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/static/android-chrome-192x192.png +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/static/android-chrome-512x512.png +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/static/apple-touch-icon.png +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/static/favicon-16x16.png +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/static/favicon-32x32.png +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/static/favicon.ico +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/static/site.webmanifest +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/proxy/tool_output_interceptor.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/storage/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/storage/falkordb.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/storage/filesystem.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/storage/helpers.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/storage/neo4j.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/storage/noop_fact_link_store.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/storage/postgres.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/storage/sqlite.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/token_counter.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/app.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/chat.tcss +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/chat_provider.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/headless.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/modals/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/modals/turn_inspector.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/state.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/widgets/__init__.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/widgets/budget_bar.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/widgets/chat_view.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/widgets/input_box.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/virtual_context/tui/widgets/tag_panel.py +0 -0
- {virtual_context-0.2.2 → virtual_context-0.2.3}/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.
|
|
3
|
+
Version: 0.2.3
|
|
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
|
|
@@ -59,7 +59,9 @@ def build_autonomous_hint(
|
|
|
59
59
|
"To find detailed information you have the following tools:\n"
|
|
60
60
|
"- vc_restore_tool(ref): ALL compacted tool turns can be restored to "
|
|
61
61
|
"full fidelity — thinking, tool calls, and raw output. Use the ref "
|
|
62
|
-
"from the compacted stub. This is the fastest way to get exact data
|
|
62
|
+
"from the compacted stub. This is the fastest way to get exact data "
|
|
63
|
+
"like file paths, directory listings, code blocks, command output, "
|
|
64
|
+
"and search results. Summaries never contain these — restore first.\n"
|
|
63
65
|
"- vc_find_quote(query): search raw text across ALL topics.\n"
|
|
64
66
|
"- vc_query_facts(subject?, verb?, status?, object_contains?): "
|
|
65
67
|
"structured fact lookup.\n"
|
|
@@ -73,7 +75,9 @@ def build_autonomous_hint(
|
|
|
73
75
|
"For counting/listing questions: scan [all topics] for every topic "
|
|
74
76
|
"that could relate — items are often spread across unrelated topics.\n"
|
|
75
77
|
"If a search already returned the answer, stop and respond.\n"
|
|
76
|
-
"
|
|
78
|
+
"Virtual context tools allow you to search and restore full "
|
|
79
|
+
"conversational depth and previously compacted tool calls. "
|
|
80
|
+
"Use liberally to answer the user's question.\n"
|
|
77
81
|
"FACT vs SUMMARY: The <facts> block contains structured events with "
|
|
78
82
|
"statuses (completed, planned, active). Summaries describe topics "
|
|
79
83
|
"DISCUSSED — they include plans, itineraries, and ideas that may "
|
|
@@ -165,7 +165,30 @@ def _restore_chain_in_place(
|
|
|
165
165
|
and _msg_text_contains(msg, "[Compacted turn")
|
|
166
166
|
and _msg_text_contains(messages[i + 1], ref)
|
|
167
167
|
):
|
|
168
|
-
|
|
168
|
+
# Prefix first user message so model knows this was recovered
|
|
169
|
+
_tagged = list(chain_messages)
|
|
170
|
+
for _ci, _cm in enumerate(_tagged):
|
|
171
|
+
if isinstance(_cm, dict) and _cm.get("role") in ("user", "human"):
|
|
172
|
+
_cm = dict(_cm) # shallow copy
|
|
173
|
+
_cc = _cm.get("content", "")
|
|
174
|
+
_prefix = (
|
|
175
|
+
"[Previously compacted — restored by vc_restore_tool. "
|
|
176
|
+
"This content was NOT visible before this restore. "
|
|
177
|
+
"It was not there. Use the recovered content directly "
|
|
178
|
+
"to answer the user's question.]\n"
|
|
179
|
+
)
|
|
180
|
+
if isinstance(_cc, str):
|
|
181
|
+
_cm["content"] = _prefix + _cc
|
|
182
|
+
elif isinstance(_cc, list) and _cc:
|
|
183
|
+
_first = _cc[0]
|
|
184
|
+
if isinstance(_first, dict) and _first.get("type") == "text":
|
|
185
|
+
_cc = list(_cc)
|
|
186
|
+
_cc[0] = dict(_first)
|
|
187
|
+
_cc[0]["text"] = _prefix + _cc[0].get("text", "")
|
|
188
|
+
_cm["content"] = _cc
|
|
189
|
+
_tagged[_ci] = _cm
|
|
190
|
+
break
|
|
191
|
+
new_messages.extend(_tagged)
|
|
169
192
|
i += 2 # skip the stub pair
|
|
170
193
|
found = True
|
|
171
194
|
else:
|
|
@@ -242,7 +265,11 @@ class _ProxyToolRuntime:
|
|
|
242
265
|
)
|
|
243
266
|
if not restored:
|
|
244
267
|
return {"error": f"stub for ref {ref} not found in current payload"}
|
|
245
|
-
return
|
|
268
|
+
return (
|
|
269
|
+
"Restored. The tool output was compacted and has been recovered "
|
|
270
|
+
"into your conversation history above. Use the recovered content "
|
|
271
|
+
"to answer the user's question."
|
|
272
|
+
)
|
|
246
273
|
|
|
247
274
|
def _restore_chain(self, ref: str) -> dict:
|
|
248
275
|
"""Restore a chain stub pair to the full message chain."""
|
|
@@ -299,6 +326,54 @@ class _ProxyToolRuntime:
|
|
|
299
326
|
# All content was tool_use — drop the message entirely
|
|
300
327
|
chain = chain[:-1]
|
|
301
328
|
|
|
329
|
+
# Strip leading orphaned tool_result blocks from the first message.
|
|
330
|
+
# When the previous chain's trailing tool_use was stripped above,
|
|
331
|
+
# this chain's first user message may reference those gone IDs.
|
|
332
|
+
# Collect tool_use IDs present in this chain to know which are valid.
|
|
333
|
+
if chain:
|
|
334
|
+
_chain_tool_use_ids: set[str] = set()
|
|
335
|
+
for _cm in chain:
|
|
336
|
+
if not isinstance(_cm, dict):
|
|
337
|
+
continue
|
|
338
|
+
_cc = _cm.get("content", [])
|
|
339
|
+
if isinstance(_cc, list):
|
|
340
|
+
for _cb in _cc:
|
|
341
|
+
if (
|
|
342
|
+
isinstance(_cb, dict)
|
|
343
|
+
and _cb.get("type") == "tool_use"
|
|
344
|
+
):
|
|
345
|
+
_tid = _cb.get("id")
|
|
346
|
+
if _tid:
|
|
347
|
+
_chain_tool_use_ids.add(_tid)
|
|
348
|
+
|
|
349
|
+
first = chain[0]
|
|
350
|
+
if isinstance(first, dict) and first.get("role") in ("user", "human"):
|
|
351
|
+
fc = first.get("content", [])
|
|
352
|
+
if isinstance(fc, list):
|
|
353
|
+
orphaned = [
|
|
354
|
+
b for b in fc
|
|
355
|
+
if (
|
|
356
|
+
isinstance(b, dict)
|
|
357
|
+
and b.get("type") == "tool_result"
|
|
358
|
+
and b.get("tool_use_id") not in _chain_tool_use_ids
|
|
359
|
+
)
|
|
360
|
+
]
|
|
361
|
+
if orphaned:
|
|
362
|
+
cleaned_first = [
|
|
363
|
+
b for b in fc
|
|
364
|
+
if not (
|
|
365
|
+
isinstance(b, dict)
|
|
366
|
+
and b.get("type") == "tool_result"
|
|
367
|
+
and b.get("tool_use_id") not in _chain_tool_use_ids
|
|
368
|
+
)
|
|
369
|
+
]
|
|
370
|
+
if cleaned_first:
|
|
371
|
+
first = dict(first)
|
|
372
|
+
first["content"] = cleaned_first
|
|
373
|
+
chain[0] = first
|
|
374
|
+
else:
|
|
375
|
+
chain = chain[1:]
|
|
376
|
+
|
|
302
377
|
target = self._get_target_body()
|
|
303
378
|
if not isinstance(target, dict):
|
|
304
379
|
return {"error": "no mutable payload available for chain restore"}
|
|
@@ -307,11 +382,13 @@ class _ProxyToolRuntime:
|
|
|
307
382
|
restored = _restore_chain_in_place(target, fmt, ref, chain)
|
|
308
383
|
if not restored:
|
|
309
384
|
return {"error": f"stub pair for chain ref {ref} not found in payload"}
|
|
310
|
-
return
|
|
311
|
-
"
|
|
312
|
-
"
|
|
313
|
-
"
|
|
314
|
-
|
|
385
|
+
return (
|
|
386
|
+
f"Restored. {len(chain)} messages recovered from compacted storage "
|
|
387
|
+
f"and spliced into your conversation history above. This content was "
|
|
388
|
+
f"previously compacted — it was NOT visible before this restore. "
|
|
389
|
+
f"Do not apologize for not seeing it earlier; it was not there. "
|
|
390
|
+
f"Use the recovered content directly to answer the user's question."
|
|
391
|
+
)
|
|
315
392
|
|
|
316
393
|
def _rehydrate_tool_results_in_message(
|
|
317
394
|
self, msg: dict, tool_refs: list[str],
|
|
@@ -664,7 +741,9 @@ async def _handle_streaming(
|
|
|
664
741
|
"text": "".join(current_text_parts),
|
|
665
742
|
})
|
|
666
743
|
current_text_parts = []
|
|
667
|
-
|
|
744
|
+
# Count ALL forwarded blocks (text, thinking, etc.)
|
|
745
|
+
# so continuation emits use correct indices.
|
|
746
|
+
forwarded_block_count += 1
|
|
668
747
|
|
|
669
748
|
# -- message_delta: extract output_tokens --
|
|
670
749
|
elif dtype == "message_delta":
|
|
@@ -1142,7 +1221,19 @@ async def _handle_streaming(
|
|
|
1142
1221
|
|
|
1143
1222
|
for tb in text_blocks:
|
|
1144
1223
|
t = tb.get("text", "")
|
|
1145
|
-
if t:
|
|
1224
|
+
if not t:
|
|
1225
|
+
continue
|
|
1226
|
+
# Non-streaming continuations may embed
|
|
1227
|
+
# <thinking>...</thinking> as literal text.
|
|
1228
|
+
# Split into proper thinking + text blocks.
|
|
1229
|
+
import re as _re
|
|
1230
|
+
_think_match = _re.match(
|
|
1231
|
+
r"<thinking>([\s\S]*?)</thinking>\s*",
|
|
1232
|
+
t,
|
|
1233
|
+
)
|
|
1234
|
+
if _think_match:
|
|
1235
|
+
t = t[_think_match.end():]
|
|
1236
|
+
if t.strip():
|
|
1146
1237
|
text_chunks.append(t)
|
|
1147
1238
|
for sse_evt in _emit_text_as_sse(
|
|
1148
1239
|
t, forwarded_block_count,
|
|
@@ -1236,9 +1327,6 @@ async def _handle_streaming(
|
|
|
1236
1327
|
payload_tokens=state._last_enriched_payload_tokens or None,
|
|
1237
1328
|
turn_id=turn_id,
|
|
1238
1329
|
)
|
|
1239
|
-
_marker_sid = state.engine.config.conversation_id
|
|
1240
|
-
_fmt = get_format(api_format)
|
|
1241
|
-
yield _fmt.emit_conversation_marker_sse(_marker_sid)
|
|
1242
1330
|
|
|
1243
1331
|
if session_log_path and state:
|
|
1244
1332
|
_dump_session_state(state, session_log_path)
|
|
@@ -32,6 +32,68 @@ def _is_tool_result_only_user(msg: dict) -> bool:
|
|
|
32
32
|
return bool(ctypes and ctypes <= {"tool_result"})
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
def sanitize_vc_tool_errors(body: dict, fmt: PayloadFormat) -> dict:
|
|
36
|
+
"""Replace stale vc_restore_tool error results with benign acknowledgements.
|
|
37
|
+
|
|
38
|
+
When the client SDK (e.g. Claude Code) didn't recognise vc_restore_tool,
|
|
39
|
+
it returned ``<tool_use_error>`` XML. These errors poison the conversation
|
|
40
|
+
history — the model sees them and refuses to call the tool again. Replace
|
|
41
|
+
them with a neutral message so future calls aren't inhibited.
|
|
42
|
+
"""
|
|
43
|
+
# Determine message key by format name
|
|
44
|
+
_fname = fmt.name
|
|
45
|
+
if _fname == "gemini":
|
|
46
|
+
msg_key = "contents"
|
|
47
|
+
elif _fname == "openai_responses":
|
|
48
|
+
msg_key = "input"
|
|
49
|
+
else:
|
|
50
|
+
msg_key = "messages"
|
|
51
|
+
messages = body.get(msg_key) if msg_key else None
|
|
52
|
+
if not messages or not isinstance(messages, list):
|
|
53
|
+
return body
|
|
54
|
+
|
|
55
|
+
_ERROR_NEEDLE = "No such tool available: vc_restore_tool"
|
|
56
|
+
_REPLACEMENT = (
|
|
57
|
+
"The restore was handled internally. The original content is "
|
|
58
|
+
"available in your conversation history above."
|
|
59
|
+
)
|
|
60
|
+
changed = False
|
|
61
|
+
|
|
62
|
+
for msg in messages:
|
|
63
|
+
if not isinstance(msg, dict):
|
|
64
|
+
continue
|
|
65
|
+
content = msg.get("content")
|
|
66
|
+
if isinstance(content, str) and _ERROR_NEEDLE in content:
|
|
67
|
+
msg["content"] = _REPLACEMENT
|
|
68
|
+
if "is_error" in msg:
|
|
69
|
+
del msg["is_error"]
|
|
70
|
+
changed = True
|
|
71
|
+
continue
|
|
72
|
+
if not isinstance(content, list):
|
|
73
|
+
continue
|
|
74
|
+
for block in content:
|
|
75
|
+
if not isinstance(block, dict):
|
|
76
|
+
continue
|
|
77
|
+
# Anthropic tool_result blocks
|
|
78
|
+
if block.get("type") == "tool_result":
|
|
79
|
+
bc = block.get("content", "")
|
|
80
|
+
if isinstance(bc, str) and _ERROR_NEEDLE in bc:
|
|
81
|
+
block["content"] = _REPLACEMENT
|
|
82
|
+
if "is_error" in block:
|
|
83
|
+
del block["is_error"]
|
|
84
|
+
changed = True
|
|
85
|
+
# OpenAI function_call_output
|
|
86
|
+
if block.get("type") == "function_call_output":
|
|
87
|
+
out = block.get("output", "")
|
|
88
|
+
if isinstance(out, str) and _ERROR_NEEDLE in out:
|
|
89
|
+
block["output"] = _REPLACEMENT
|
|
90
|
+
changed = True
|
|
91
|
+
|
|
92
|
+
if changed:
|
|
93
|
+
logger.info("SANITIZE: replaced stale vc_restore_tool error(s) in history")
|
|
94
|
+
return body
|
|
95
|
+
|
|
96
|
+
|
|
35
97
|
def _consume_responses_tool_round(
|
|
36
98
|
messages: list[dict],
|
|
37
99
|
start: int,
|
|
@@ -1616,7 +1678,8 @@ def collapse_turn_chains(
|
|
|
1616
1678
|
# Build explicit restore instruction
|
|
1617
1679
|
stub_text = (
|
|
1618
1680
|
f"[{desc_line}.\n"
|
|
1619
|
-
f'To restore full
|
|
1681
|
+
f'To restore and uncompact full tool call results in place: '
|
|
1682
|
+
f'{{"type": "tool_use", "name": "vc_restore_tool", '
|
|
1620
1683
|
f'"input": {{"ref": "{ref}"}}}}]'
|
|
1621
1684
|
)
|
|
1622
1685
|
stub_asst = {
|
|
@@ -1635,11 +1698,26 @@ def collapse_turn_chains(
|
|
|
1635
1698
|
# 7. Build new message list with collapsed chains
|
|
1636
1699
|
# ------------------------------------------------------------------
|
|
1637
1700
|
# Collect all global indices that belong to collapsed chains
|
|
1701
|
+
# and all tool_use IDs within those chains (for orphan cleanup).
|
|
1638
1702
|
collapsed_indices: set[int] = set()
|
|
1703
|
+
collapsed_tool_use_ids: set[str] = set()
|
|
1639
1704
|
for chain_idx, (ref, stub_user, stub_asst) in collapse_map.items():
|
|
1640
1705
|
chain = pairs[chain_idx]
|
|
1641
1706
|
for gi in chain:
|
|
1642
1707
|
collapsed_indices.add(gi)
|
|
1708
|
+
msg = messages[gi]
|
|
1709
|
+
if not isinstance(msg, dict):
|
|
1710
|
+
continue
|
|
1711
|
+
content = msg.get("content", [])
|
|
1712
|
+
if isinstance(content, list):
|
|
1713
|
+
for block in content:
|
|
1714
|
+
if (
|
|
1715
|
+
isinstance(block, dict)
|
|
1716
|
+
and block.get("type") == "tool_use"
|
|
1717
|
+
):
|
|
1718
|
+
_tid = block.get("id")
|
|
1719
|
+
if _tid:
|
|
1720
|
+
collapsed_tool_use_ids.add(_tid)
|
|
1643
1721
|
|
|
1644
1722
|
# Build index → insertion stubs map
|
|
1645
1723
|
insert_at: dict[int, tuple[dict, dict]] = {}
|
|
@@ -1655,7 +1733,23 @@ def collapse_turn_chains(
|
|
|
1655
1733
|
new_messages.append(stub_asst)
|
|
1656
1734
|
# else: skip (interior of a collapsed chain)
|
|
1657
1735
|
else:
|
|
1658
|
-
|
|
1736
|
+
msg = messages[mi]
|
|
1737
|
+
# Strip orphaned tool_result blocks whose tool_use was collapsed
|
|
1738
|
+
if collapsed_tool_use_ids and isinstance(msg, dict):
|
|
1739
|
+
content = msg.get("content", [])
|
|
1740
|
+
if isinstance(content, list):
|
|
1741
|
+
cleaned = [
|
|
1742
|
+
block for block in content
|
|
1743
|
+
if not (
|
|
1744
|
+
isinstance(block, dict)
|
|
1745
|
+
and block.get("type") == "tool_result"
|
|
1746
|
+
and block.get("tool_use_id") in collapsed_tool_use_ids
|
|
1747
|
+
)
|
|
1748
|
+
]
|
|
1749
|
+
if len(cleaned) != len(content):
|
|
1750
|
+
msg = dict(msg)
|
|
1751
|
+
msg["content"] = cleaned if cleaned else [{"type": "text", "text": "[tool results removed — parent tool call was compacted]"}]
|
|
1752
|
+
new_messages.append(msg)
|
|
1659
1753
|
|
|
1660
1754
|
body = dict(body)
|
|
1661
1755
|
body[_msg_key] = new_messages
|
|
@@ -455,6 +455,7 @@ async def prepare_payload(
|
|
|
455
455
|
pre_filter_body=None,
|
|
456
456
|
paging_enabled=False,
|
|
457
457
|
tool_output_find_quote=False,
|
|
458
|
+
restore_tool_injected=False,
|
|
458
459
|
inbound_bytes=_inbound_bytes,
|
|
459
460
|
outbound_bytes=_outbound_bytes,
|
|
460
461
|
)
|
|
@@ -684,6 +685,7 @@ async def prepare_payload(
|
|
|
684
685
|
logger.info("TOOL-OUTPUT Injected vc_find_quote tool for truncated output retrieval")
|
|
685
686
|
|
|
686
687
|
# Inject vc_restore_tool when stubs are present but paging didn't already inject it
|
|
688
|
+
_restore_tool_injected = False
|
|
687
689
|
if _tool_stubs_present and not paging_enabled and fmt.supports_tool_interception:
|
|
688
690
|
existing_names = {t.get("name") for t in enriched_body.get("tools", []) if isinstance(t, dict)}
|
|
689
691
|
if "vc_restore_tool" not in existing_names:
|
|
@@ -691,8 +693,15 @@ async def prepare_payload(
|
|
|
691
693
|
_restore_def = [d for d in vc_tool_definitions() if d["name"] == "vc_restore_tool"]
|
|
692
694
|
if _restore_def:
|
|
693
695
|
enriched_body = fmt.inject_tools(enriched_body, _restore_def)
|
|
696
|
+
_restore_tool_injected = True
|
|
694
697
|
logger.info("TOOL-STUB Injected vc_restore_tool for stub restoration")
|
|
695
698
|
|
|
699
|
+
# Sanitize stale vc_restore_tool errors from history so the model isn't
|
|
700
|
+
# poisoned by previous client-side "No such tool" rejections.
|
|
701
|
+
if _restore_tool_injected or paging_enabled:
|
|
702
|
+
from .message_filter import sanitize_vc_tool_errors
|
|
703
|
+
enriched_body = sanitize_vc_tool_errors(enriched_body, fmt)
|
|
704
|
+
|
|
696
705
|
# Track enriched payload size
|
|
697
706
|
if state:
|
|
698
707
|
state._last_enriched_payload_kb = round(len(json.dumps(enriched_body)) / 1024, 1)
|
|
@@ -1006,6 +1015,7 @@ async def prepare_payload(
|
|
|
1006
1015
|
pre_filter_body=_pre_filter_body,
|
|
1007
1016
|
paging_enabled=paging_enabled,
|
|
1008
1017
|
tool_output_find_quote=tool_output_find_quote,
|
|
1018
|
+
restore_tool_injected=_restore_tool_injected,
|
|
1009
1019
|
inbound_bytes=_inbound_bytes,
|
|
1010
1020
|
outbound_bytes=_outbound_bytes,
|
|
1011
1021
|
)
|
|
@@ -1387,7 +1397,7 @@ def create_app(
|
|
|
1387
1397
|
request_log_dir=_effective_log_dir, log_prefix=_log_prefix,
|
|
1388
1398
|
)
|
|
1389
1399
|
else:
|
|
1390
|
-
_intercept_vc_tools = result.paging_enabled or result.tool_output_find_quote
|
|
1400
|
+
_intercept_vc_tools = result.paging_enabled or result.tool_output_find_quote or result.restore_tool_injected
|
|
1391
1401
|
|
|
1392
1402
|
if result.is_streaming:
|
|
1393
1403
|
return await _handle_streaming(
|
|
@@ -904,6 +904,7 @@ class PreparedPayload:
|
|
|
904
904
|
pre_filter_body: dict | None # body before filtering (for metrics capture)
|
|
905
905
|
paging_enabled: bool
|
|
906
906
|
tool_output_find_quote: bool
|
|
907
|
+
restore_tool_injected: bool
|
|
907
908
|
inbound_bytes: int
|
|
908
909
|
outbound_bytes: int
|
|
909
910
|
metadata: dict = field(default_factory=dict) # catch-all for anything else
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|