virtual-context 0.3.8__tar.gz → 0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. {virtual_context-0.3.8 → virtual_context-0.4}/CLAUDE.md +3 -0
  2. {virtual_context-0.3.8 → virtual_context-0.4}/PKG-INFO +1 -1
  3. {virtual_context-0.3.8 → virtual_context-0.4}/pyproject.toml +1 -1
  4. {virtual_context-0.3.8 → virtual_context-0.4}/specs/cache-aware-compaction.md +93 -93
  5. {virtual_context-0.3.8 → virtual_context-0.4}/tests/haiku/test_compaction.py +3 -3
  6. virtual_context-0.4/tests/test_active_compaction_op_invariants.py +172 -0
  7. virtual_context-0.4/tests/test_canonical_row_epoch_guards.py +224 -0
  8. virtual_context-0.4/tests/test_canonical_row_epoch_guards_postgres.py +200 -0
  9. virtual_context-0.4/tests/test_canonical_turn_grouping.py +206 -0
  10. virtual_context-0.4/tests/test_canonical_turns_schema.py +32 -0
  11. virtual_context-0.4/tests/test_canonical_turns_schema_postgres.py +47 -0
  12. virtual_context-0.4/tests/test_claim_compaction_lease_signature.py +147 -0
  13. virtual_context-0.4/tests/test_claim_compaction_lease_signature_postgres.py +114 -0
  14. virtual_context-0.4/tests/test_cleanup_abandoned_compaction.py +329 -0
  15. virtual_context-0.4/tests/test_cleanup_abandoned_compaction_postgres.py +381 -0
  16. virtual_context-0.4/tests/test_compacting_phase_takeover.py +834 -0
  17. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_compaction_commit_prune.py +3 -23
  18. virtual_context-0.4/tests/test_compaction_heartbeat_sidecar.py +139 -0
  19. virtual_context-0.4/tests/test_compaction_lease_lost_abort.py +54 -0
  20. virtual_context-0.4/tests/test_compaction_lifecycle.py +170 -0
  21. virtual_context-0.4/tests/test_compaction_lifecycle_contract.py +69 -0
  22. virtual_context-0.4/tests/test_compaction_lifecycle_wiring.py +167 -0
  23. virtual_context-0.4/tests/test_compaction_operation.py +80 -0
  24. virtual_context-0.4/tests/test_compaction_operation_crud.py +396 -0
  25. virtual_context-0.4/tests/test_compaction_operation_crud_postgres.py +400 -0
  26. virtual_context-0.4/tests/test_compaction_operation_id_uuid.py +94 -0
  27. virtual_context-0.4/tests/test_compaction_operation_postgres.py +50 -0
  28. virtual_context-0.4/tests/test_compaction_ownership_guards.py +742 -0
  29. virtual_context-0.4/tests/test_compaction_progress_event.py +84 -0
  30. virtual_context-0.4/tests/test_compaction_resume_e2e_postgres.py +342 -0
  31. virtual_context-0.4/tests/test_compaction_resume_schema_migration.py +99 -0
  32. virtual_context-0.4/tests/test_compaction_resume_schema_migration_postgres.py +173 -0
  33. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_composite_store.py +12 -2
  34. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_conversation_coverage_report.py +44 -2
  35. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_conversation_lifecycle.py +7 -5
  36. virtual_context-0.4/tests/test_conversation_schema.py +19 -0
  37. virtual_context-0.4/tests/test_conversation_schema_postgres.py +35 -0
  38. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_deferred_payload_compaction.py +174 -77
  39. virtual_context-0.4/tests/test_delete_resurrect_epoch_races.py +179 -0
  40. virtual_context-0.4/tests/test_engine_event_bus.py +65 -0
  41. virtual_context-0.4/tests/test_engine_lifecycle_epoch.py +146 -0
  42. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_engine_state.py +210 -40
  43. virtual_context-0.4/tests/test_engine_sync_turns.py +844 -0
  44. virtual_context-0.4/tests/test_filesystem_store_compaction_compat.py +172 -0
  45. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_find_quote.py +26 -26
  46. virtual_context-0.4/tests/test_find_stale_lease_helpers.py +192 -0
  47. virtual_context-0.4/tests/test_handle_prepare_payload.py +707 -0
  48. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_history_filter.py +1 -1
  49. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_ingest_index_integrity.py +8 -8
  50. virtual_context-0.4/tests/test_ingest_reconciler_append_fastpath.py +390 -0
  51. virtual_context-0.4/tests/test_ingest_reconciler_epoch.py +142 -0
  52. virtual_context-0.4/tests/test_ingestion_compaction_full_cycle.py +227 -0
  53. virtual_context-0.4/tests/test_ingestion_db_sweep_catchup.py +222 -0
  54. virtual_context-0.4/tests/test_ingestion_episode.py +74 -0
  55. virtual_context-0.4/tests/test_ingestion_episode_crud.py +198 -0
  56. virtual_context-0.4/tests/test_ingestion_episode_crud_postgres.py +301 -0
  57. virtual_context-0.4/tests/test_ingestion_episode_postgres.py +50 -0
  58. virtual_context-0.4/tests/test_ingestion_finalize_self_heal.py +291 -0
  59. virtual_context-0.4/tests/test_ingestion_progress_event.py +75 -0
  60. virtual_context-0.4/tests/test_ingestion_regression_resume.py +471 -0
  61. virtual_context-0.4/tests/test_legacy_ingestion_lease_ownership.py +1095 -0
  62. virtual_context-0.4/tests/test_lifecycle_epoch.py +47 -0
  63. virtual_context-0.4/tests/test_lifecycle_epoch_store.py +221 -0
  64. virtual_context-0.4/tests/test_lifecycle_epoch_store_postgres.py +290 -0
  65. virtual_context-0.4/tests/test_mark_canonical_turns_compacted_all_halves.py +194 -0
  66. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_monitor.py +1 -1
  67. virtual_context-0.4/tests/test_multi_worker_sliding_window.py +247 -0
  68. virtual_context-0.4/tests/test_neo4j_falkordb_compaction_compat.py +81 -0
  69. virtual_context-0.4/tests/test_orphan_compacting_repair.py +102 -0
  70. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_paging.py +10 -10
  71. virtual_context-0.4/tests/test_phase_transition_events.py +112 -0
  72. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_postgres_store.py +75 -0
  73. virtual_context-0.4/tests/test_progress_event_bus.py +91 -0
  74. virtual_context-0.4/tests/test_progress_events.py +74 -0
  75. virtual_context-0.4/tests/test_progress_snapshot.py +202 -0
  76. virtual_context-0.4/tests/test_progress_snapshot_postgres.py +334 -0
  77. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_proxy.py +175 -5
  78. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_proxy_dashboard.py +6 -6
  79. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_proxy_message_filter.py +6 -6
  80. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_proxy_session.py +277 -8
  81. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_proxy_streaming.py +1 -1
  82. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_raw_content.py +14 -17
  83. virtual_context-0.4/tests/test_refresh_compaction_heartbeat.py +85 -0
  84. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_request_captures_persistence.py +110 -18
  85. virtual_context-0.4/tests/test_request_metadata.py +187 -0
  86. virtual_context-0.4/tests/test_request_metadata_postgres.py +196 -0
  87. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_semantic_search.py +7 -7
  88. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_sender_identity.py +2 -2
  89. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_session_date.py +109 -0
  90. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_session_state.py +31 -3
  91. virtual_context-0.4/tests/test_sqlite_schema_add_column_race.py +99 -0
  92. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_store_recovery.py +9 -8
  93. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_tag_splitter.py +4 -4
  94. virtual_context-0.4/tests/test_tagger_loop.py +231 -0
  95. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_tool_loop.py +1 -1
  96. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_tool_result_filter.py +7 -8
  97. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_tool_tags.py +4 -4
  98. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_turn_tag_index.py +2 -2
  99. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_vcattach.py +20 -9
  100. virtual_context-0.4/tests_by_section.sh +248 -0
  101. virtual_context-0.4/tests_timing_report.md +72 -0
  102. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/__init__.py +1 -1
  103. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/cli/main.py +2 -3
  104. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/assembler.py +6 -1
  105. virtual_context-0.4/virtual_context/core/canonical_turns.py +96 -0
  106. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/compaction_pipeline.py +253 -124
  107. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/compactor.py +18 -2
  108. virtual_context-0.4/virtual_context/core/composite_store.py +1096 -0
  109. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/conversation_store.py +10 -6
  110. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/coverage_report.py +12 -2
  111. virtual_context-0.4/virtual_context/core/event_bus.py +43 -0
  112. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/hint_builder.py +10 -11
  113. virtual_context-0.4/virtual_context/core/ingest_reconciler.py +844 -0
  114. virtual_context-0.4/virtual_context/core/lifecycle_epoch.py +25 -0
  115. virtual_context-0.4/virtual_context/core/progress_events.py +37 -0
  116. virtual_context-0.4/virtual_context/core/progress_snapshot.py +40 -0
  117. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/protocols.py +109 -24
  118. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/provider_adapters.py +12 -4
  119. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/quote_search.py +12 -29
  120. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/retrieval_assembler.py +9 -6
  121. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/search_engine.py +3 -3
  122. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/segmenter.py +1 -1
  123. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/semantic_search.py +26 -36
  124. virtual_context-0.4/virtual_context/core/store.py +1112 -0
  125. virtual_context-0.4/virtual_context/core/tagging_pipeline.py +1514 -0
  126. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/tool_query.py +1 -1
  127. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/turn_tag_index.py +38 -6
  128. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/engine.py +524 -299
  129. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/dashboard.html +7 -7
  130. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/dashboard.py +8 -8
  131. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/formats.py +237 -115
  132. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/handlers.py +37 -19
  133. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/helpers.py +17 -6
  134. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/message_filter.py +193 -27
  135. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/metrics.py +29 -8
  136. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/server.py +221 -61
  137. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/session_state.py +65 -21
  138. virtual_context-0.4/virtual_context/proxy/state.py +3854 -0
  139. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/storage/falkordb.py +18 -2
  140. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/storage/filesystem.py +37 -129
  141. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/storage/neo4j.py +18 -2
  142. virtual_context-0.4/virtual_context/storage/postgres.py +5302 -0
  143. virtual_context-0.4/virtual_context/storage/sqlite.py +5987 -0
  144. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/types.py +105 -29
  145. virtual_context-0.3.8/tests/test_engine_sync_turns.py +0 -195
  146. virtual_context-0.3.8/tests/test_full_text_backfill.py +0 -478
  147. virtual_context-0.3.8/virtual_context/core/composite_store.py +0 -546
  148. virtual_context-0.3.8/virtual_context/core/full_text_backfill.py +0 -691
  149. virtual_context-0.3.8/virtual_context/core/store.py +0 -627
  150. virtual_context-0.3.8/virtual_context/core/tagging_pipeline.py +0 -907
  151. virtual_context-0.3.8/virtual_context/proxy/state.py +0 -1967
  152. virtual_context-0.3.8/virtual_context/storage/postgres.py +0 -2900
  153. virtual_context-0.3.8/virtual_context/storage/sqlite.py +0 -3676
  154. {virtual_context-0.3.8 → virtual_context-0.4}/.github/workflows/python-publish.yml +0 -0
  155. {virtual_context-0.3.8 → virtual_context-0.4}/.gitignore +0 -0
  156. {virtual_context-0.3.8 → virtual_context-0.4}/LICENSE +0 -0
  157. {virtual_context-0.3.8 → virtual_context-0.4}/README.md +0 -0
  158. {virtual_context-0.3.8 → virtual_context-0.4}/assets/dashboard.png +0 -0
  159. {virtual_context-0.3.8 → virtual_context-0.4}/assets/hero.png +0 -0
  160. {virtual_context-0.3.8 → virtual_context-0.4}/models.yaml +0 -0
  161. {virtual_context-0.3.8 → virtual_context-0.4}/scripts/install.ps1 +0 -0
  162. {virtual_context-0.3.8 → virtual_context-0.4}/scripts/install.sh +0 -0
  163. {virtual_context-0.3.8 → virtual_context-0.4}/tests/REGRESSION_MAP.md +0 -0
  164. {virtual_context-0.3.8 → virtual_context-0.4}/tests/conftest.py +0 -0
  165. {virtual_context-0.3.8 → virtual_context-0.4}/tests/docker-compose.test.yml +0 -0
  166. {virtual_context-0.3.8 → virtual_context-0.4}/tests/haiku/__init__.py +0 -0
  167. {virtual_context-0.3.8 → virtual_context-0.4}/tests/haiku/conftest.py +0 -0
  168. {virtual_context-0.3.8 → virtual_context-0.4}/tests/haiku/test_retrieval.py +0 -0
  169. {virtual_context-0.3.8 → virtual_context-0.4}/tests/haiku/test_tagging.py +0 -0
  170. {virtual_context-0.3.8 → virtual_context-0.4}/tests/ollama/__init__.py +0 -0
  171. {virtual_context-0.3.8 → virtual_context-0.4}/tests/ollama/conftest.py +0 -0
  172. {virtual_context-0.3.8 → virtual_context-0.4}/tests/ollama/test_compactor.py +0 -0
  173. {virtual_context-0.3.8 → virtual_context-0.4}/tests/ollama/test_pipeline.py +0 -0
  174. {virtual_context-0.3.8 → virtual_context-0.4}/tests/ollama/test_provider.py +0 -0
  175. {virtual_context-0.3.8 → virtual_context-0.4}/tests/ollama/test_tag_generator.py +0 -0
  176. {virtual_context-0.3.8 → virtual_context-0.4}/tests/proxy/__init__.py +0 -0
  177. {virtual_context-0.3.8 → virtual_context-0.4}/tests/proxy/test_dashboard_cors.py +0 -0
  178. {virtual_context-0.3.8 → virtual_context-0.4}/tests/proxy/test_metrics.py +0 -0
  179. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_assembler.py +0 -0
  180. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_backend_integration.py +0 -0
  181. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_beam_judge.py +0 -0
  182. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_budget_enforcement.py +0 -0
  183. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_cli_init.py +0 -0
  184. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_compactor.py +0 -0
  185. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_compactor_concurrent.py +0 -0
  186. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_config.py +0 -0
  187. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_context_bleed.py +0 -0
  188. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_conversation_identity.py +0 -0
  189. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_conversation_scoping.py +0 -0
  190. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_embedding_tag_generator.py +0 -0
  191. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_empty_turn_skip.py +0 -0
  192. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_engine_integration.py +0 -0
  193. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_engine_lookback.py +0 -0
  194. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_fact_enrichment.py +0 -0
  195. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_fact_graph_integration.py +0 -0
  196. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_fact_link_checker.py +0 -0
  197. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_fact_link_query.py +0 -0
  198. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_fact_link_types.py +0 -0
  199. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_fact_links_sqlite.py +0 -0
  200. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_fact_redesign.py +0 -0
  201. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_fill_pass.py +0 -0
  202. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_format_agnostic.py +0 -0
  203. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_headless.py +0 -0
  204. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_idf_retrieval.py +0 -0
  205. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_longmemeval_auth.py +0 -0
  206. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_mcp_server.py +0 -0
  207. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_media.py +0 -0
  208. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_message_filter.py +0 -0
  209. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_metrics_persistence.py +0 -0
  210. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_model_catalog.py +0 -0
  211. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_model_limits.py +0 -0
  212. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_multi_instance.py +0 -0
  213. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_noop_fact_link_store.py +0 -0
  214. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_openrouter_provider.py +0 -0
  215. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_passthrough_filter.py +0 -0
  216. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_presets.py +0 -0
  217. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_prev_context_leak.py +0 -0
  218. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_provider_adapters.py +0 -0
  219. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_proxy_formats.py +0 -0
  220. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_recall_all.py +0 -0
  221. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_registry_lifecycle.py +0 -0
  222. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_retriever.py +0 -0
  223. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_rrf_scoring.py +0 -0
  224. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_segmenter.py +0 -0
  225. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_session_cache.py +0 -0
  226. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_storage_protocols.py +0 -0
  227. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_store_sqlite.py +0 -0
  228. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_stub_turn_handling.py +0 -0
  229. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_supersession.py +0 -0
  230. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_supersession_migration.py +0 -0
  231. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_tag_canonicalizer.py +0 -0
  232. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_tag_consolidator.py +0 -0
  233. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_tag_generator.py +0 -0
  234. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_telemetry.py +0 -0
  235. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_telemetry_integration.py +0 -0
  236. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_temporal_resolver.py +0 -0
  237. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_tool_output_interceptor.py +0 -0
  238. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_tui.py +0 -0
  239. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_turn_grouping.py +0 -0
  240. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_unified_budget.py +0 -0
  241. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_upstream_trim.py +0 -0
  242. {virtual_context-0.3.8 → virtual_context-0.4}/tests/test_verb_expansion.py +0 -0
  243. {virtual_context-0.3.8 → virtual_context-0.4}/virtual-context.yaml +0 -0
  244. {virtual_context-0.3.8 → virtual_context-0.4}/virtual-context.yaml.example +0 -0
  245. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/cli/__init__.py +0 -0
  246. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/config.py +0 -0
  247. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/conversation_identity.py +0 -0
  248. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/__init__.py +0 -0
  249. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/embedding_provider.py +0 -0
  250. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/embedding_tag_generator.py +0 -0
  251. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/engine_utils.py +0 -0
  252. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/fact_query.py +0 -0
  253. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/fts_preprocessor.py +0 -0
  254. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/llm_utils.py +0 -0
  255. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/math_utils.py +0 -0
  256. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/model_catalog.py +0 -0
  257. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/monitor.py +0 -0
  258. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/paging_manager.py +0 -0
  259. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/retrieval_scoring.py +0 -0
  260. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/retriever.py +0 -0
  261. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/tag_canonicalizer.py +0 -0
  262. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/tag_consolidator.py +0 -0
  263. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/tag_generator.py +0 -0
  264. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/tag_scoring.py +0 -0
  265. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/tag_splitter.py +0 -0
  266. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/telemetry.py +0 -0
  267. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/temporal_resolver.py +0 -0
  268. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/core/tool_loop.py +0 -0
  269. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/data/anthropic-tokenizer/tokenizer.json +0 -0
  270. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/data/models.yaml +0 -0
  271. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/ingest/__init__.py +0 -0
  272. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/ingest/curator.py +0 -0
  273. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/ingest/date_resolver.py +0 -0
  274. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/ingest/parsers.py +0 -0
  275. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/ingest/supersession.py +0 -0
  276. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/mcp/__init__.py +0 -0
  277. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/mcp/server.py +0 -0
  278. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/model_limits.py +0 -0
  279. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/openclaw/virtual-context.mjs +0 -0
  280. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/patterns.py +0 -0
  281. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/presets/__init__.py +0 -0
  282. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/presets/agentic.py +0 -0
  283. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/presets/base.py +0 -0
  284. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/presets/coding.py +0 -0
  285. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/providers/__init__.py +0 -0
  286. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/providers/anthropic.py +0 -0
  287. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/providers/base.py +0 -0
  288. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/providers/generic_openai.py +0 -0
  289. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/providers/ollama_native.py +0 -0
  290. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/__init__.py +0 -0
  291. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/_envelope.py +0 -0
  292. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/media.py +0 -0
  293. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/multi.py +0 -0
  294. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/registry.py +0 -0
  295. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/session_cache.py +0 -0
  296. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/static/android-chrome-192x192.png +0 -0
  297. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/static/android-chrome-512x512.png +0 -0
  298. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/static/apple-touch-icon.png +0 -0
  299. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/static/favicon-16x16.png +0 -0
  300. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/static/favicon-32x32.png +0 -0
  301. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/static/favicon.ico +0 -0
  302. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/static/site.webmanifest +0 -0
  303. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/tool_output_interceptor.py +0 -0
  304. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/proxy/vcattach.py +0 -0
  305. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/storage/__init__.py +0 -0
  306. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/storage/helpers.py +0 -0
  307. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/storage/noop_fact_link_store.py +0 -0
  308. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/token_counter.py +0 -0
  309. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/__init__.py +0 -0
  310. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/app.py +0 -0
  311. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/chat.tcss +0 -0
  312. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/chat_provider.py +0 -0
  313. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/headless.py +0 -0
  314. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/modals/__init__.py +0 -0
  315. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/modals/turn_inspector.py +0 -0
  316. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/state.py +0 -0
  317. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/widgets/__init__.py +0 -0
  318. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/widgets/budget_bar.py +0 -0
  319. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/widgets/chat_view.py +0 -0
  320. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/widgets/input_box.py +0 -0
  321. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/widgets/tag_panel.py +0 -0
  322. {virtual_context-0.3.8 → virtual_context-0.4}/virtual_context/tui/widgets/turn_list.py +0 -0
@@ -1,5 +1,8 @@
1
1
  # Virtual Context
2
2
 
3
+ ## WORKFLOW: ONE ISSUE AT A TIME
4
+ Work through benchmark issues one at a time. Do not move on from an issue without an explicit directive from the user. Never suggest moving on to the next issue. The user will decide when to move on.
5
+
3
6
  ## Environment
4
7
 
5
8
  Always run tests with `.venv/bin/pytest`. Always use `.venv/bin/python` for any Python commands. The project venv must be used for all operations.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: virtual-context
3
- Version: 0.3.8
3
+ Version: 0.4
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "virtual-context"
7
- version = "0.3.8"
7
+ version = "0.4"
8
8
  description = "OS-style virtual memory for LLM session context management"
9
9
  readme = "README.md"
10
10
  license = "AGPL-3.0-or-later"
@@ -18,8 +18,8 @@ Split every historical-payload mutation into two phases:
18
18
 
19
19
  | Phase | When | Where | What it does |
20
20
  |-------|------|-------|-------------|
21
- | **Prepare** | Soft threshold (background) | Compaction pipeline | Summarizes text, archives tool outputs, archives media references, advances `compacted_through` |
22
- | **Flush** | Hard threshold or cache-cold (request-time) | Server request path | Drops raw turns, stubs tool outputs, collapses chains, stubs media, switches to post-flush mode, advances `flushed_through` |
21
+ | **Prepare** | Soft threshold (background) | Compaction pipeline | Summarizes text, archives tool outputs, archives media references, advances `compacted_prefix_messages` |
22
+ | **Flush** | Hard threshold or cache-cold (request-time) | Server request path | Drops raw turns, stubs tool outputs, collapses chains, stubs media, switches to post-flush mode, advances `flushed_prefix_messages` |
23
23
 
24
24
  **Core invariant**: While the cache is warm and the payload fits within the hard threshold, the outbound request body is byte-identical to the previous request for all historical content. No operation may rewrite historical messages until the flush gate fires.
25
25
 
@@ -30,30 +30,30 @@ Split every historical-payload mutation into two phases:
30
30
  Two watermarks track compaction progress independently:
31
31
 
32
32
  ```
33
- compacted_through — "archives exist up to here" (set by background compactor)
34
- flushed_through — "raw content removed up to here" (set by flush gate)
33
+ compacted_prefix_messages — "archives exist up to here" (set by background compactor)
34
+ flushed_prefix_messages — "raw content removed up to here" (set by flush gate)
35
35
  ```
36
36
 
37
- **Invariant**: `0 <= flushed_through <= compacted_through` always.
37
+ **Invariant**: `0 <= flushed_prefix_messages <= compacted_prefix_messages` always.
38
38
 
39
39
  ### Lifecycle
40
40
 
41
41
  ```
42
42
  Turn 0 Turn 20 Turn 40 Turn 80
43
43
  | | | |
44
- |<-- flushed_through=0 | | (no mutations applied)
44
+ |<-- flushed_prefix_messages=0 | | (no mutations applied)
45
45
  | | | |
46
- |<------- compacted_through=40 | (summaries + archives ready)
46
+ |<------- compacted_prefix_messages=40 | (summaries + archives ready)
47
47
  | | | |
48
48
  | | | |
49
49
  [--- flush fires at hard threshold ---]
50
50
  | | | |
51
- |<------- flushed_through=40 | (raw content removed)
51
+ |<------- flushed_prefix_messages=40 | (raw content removed)
52
52
  ```
53
53
 
54
54
  ### State transitions
55
55
 
56
- | Event | `compacted_through` | `flushed_through` | Payload |
56
+ | Event | `compacted_prefix_messages` | `flushed_prefix_messages` | Payload |
57
57
  |-------|--------------------|--------------------|---------|
58
58
  | Initial | 0 | 0 | Raw history |
59
59
  | Prepare runs at soft threshold | N | 0 | Unchanged (cache-stable) |
@@ -61,15 +61,15 @@ flushed_through — "raw content removed up to here" (set by flush gate)
61
61
  | Flush fires (hard threshold or cache-cold) | M | M | Shrunk: summaries replace raw content |
62
62
  | Prepare runs again | P > M | M | Unchanged until next flush |
63
63
 
64
- ### What `compacted_through` means now
64
+ ### What `compacted_prefix_messages` means now
65
65
 
66
- `compacted_through` represents the message index up to which ALL archival artifacts exist:
66
+ `compacted_prefix_messages` represents the message index up to which ALL archival artifacts exist:
67
67
  - Text summaries/segments are stored for these turns
68
68
  - Tool outputs in this range have been archived to the store (or will be archived idempotently at flush time by the existing helpers)
69
69
  - Media positions in this range are known
70
70
  - It is safe to flush any content up to this boundary
71
71
 
72
- `flushed_through` represents the message index up to which raw content has actually been removed from the outbound payload and replaced with compacted representations.
72
+ `flushed_prefix_messages` represents the message index up to which raw content has actually been removed from the outbound payload and replaced with compacted representations.
73
73
 
74
74
  ---
75
75
 
@@ -95,7 +95,7 @@ class MonitorConfig:
95
95
  When `defer_payload_mutation` is **False** (default):
96
96
  - All behavior is identical to today
97
97
  - `soft_threshold` / `hard_threshold` keep their existing defaults (0.70 / 0.85)
98
- - `flushed_through` tracks `compacted_through` automatically (set to `compacted_through` on every request)
98
+ - `flushed_prefix_messages` tracks `compacted_prefix_messages` automatically (set to `compacted_prefix_messages` on every request)
99
99
  - All existing code paths run unchanged
100
100
  - `flush_ttl_seconds` is ignored
101
101
 
@@ -122,15 +122,15 @@ Operators who enable deferred mode without adjusting thresholds get: compaction
122
122
  @dataclass
123
123
  class EngineState:
124
124
  # Existing fields unchanged
125
- compacted_through: int = 0 # semantics broadened: "all archives ready up to here"
125
+ compacted_prefix_messages: int = 0 # semantics broadened: "all archives ready up to here"
126
126
  # ...
127
127
 
128
128
  # NEW
129
- flushed_through: int = 0 # "raw content removed from payload up to here"
129
+ flushed_prefix_messages: int = 0 # "raw content removed from payload up to here"
130
130
  last_request_time: float = 0.0 # time.time() of last COMPLETED upstream call
131
131
  ```
132
132
 
133
- **Invariant enforcement**: On every update to `flushed_through`, assert `flushed_through <= compacted_through`. If violated, log error and clamp.
133
+ **Invariant enforcement**: On every update to `flushed_prefix_messages`, assert `flushed_prefix_messages <= compacted_prefix_messages`. If violated, log error and clamp.
134
134
 
135
135
  ---
136
136
 
@@ -144,8 +144,8 @@ Runs at the soft threshold via the existing compaction pipeline. The compactor a
144
144
  |----------|-----|---------|
145
145
  | Text summaries / segments | Existing compaction pipeline (`compaction_pipeline.py`) | Tag summaries in store |
146
146
  | Tool-output archives | Existing helpers archive tool content to store as a side effect of stubbing. For prepare-only mode, the compaction pipeline should pre-archive tool outputs in the compacted range so flush-time archival is a no-op. **Fallback**: the flush-time helpers (`stub_tool_outputs_by_position`, `collapse_turn_chains`) archive idempotently before replacing, so flush still works correctly even if pre-archival was skipped. |
147
- | Media position metadata | Lightweight: the compacted range boundary (`compacted_through`) is sufficient. `stub_media_by_position()` uses the protected window and turn groups to identify stale media at flush time. No separate media index needed. |
148
- | Flushable range metadata | `compacted_through` itself — everything below this boundary has archives and can be safely flushed |
147
+ | Media position metadata | Lightweight: the compacted range boundary (`compacted_prefix_messages`) is sufficient. `stub_media_by_position()` uses the protected window and turn groups to identify stale media at flush time. No separate media index needed. |
148
+ | Flushable range metadata | `compacted_prefix_messages` itself — everything below this boundary has archives and can be safely flushed |
149
149
 
150
150
  ### What the prepare phase must NOT do
151
151
 
@@ -157,7 +157,7 @@ Runs at the soft threshold via the existing compaction pipeline. The compactor a
157
157
  - Must not inject paging tools or context hints that reference compacted state
158
158
  - Must not modify the outbound request body in any way
159
159
 
160
- The prepare phase advances `compacted_through` only. All payload mutations are reserved for the flush gate.
160
+ The prepare phase advances `compacted_prefix_messages` only. All payload mutations are reserved for the flush gate.
161
161
 
162
162
  ---
163
163
 
@@ -170,12 +170,12 @@ The flush gate runs on every inbound request and decides whether to apply payloa
170
170
  ```python
171
171
  if not mon.defer_payload_mutation:
172
172
  # Legacy mode: flush immediately on every request (today's behavior)
173
- should_flush = (compacted_through > flushed_through)
173
+ should_flush = (compacted_prefix_messages > flushed_prefix_messages)
174
174
  use_two_pass = False
175
175
 
176
176
  elif cache_age >= mon.flush_ttl_seconds:
177
177
  # Cache is cold — flush in pass 1 directly (fast path)
178
- should_flush = (compacted_through > flushed_through)
178
+ should_flush = (compacted_prefix_messages > flushed_prefix_messages)
179
179
  use_two_pass = False
180
180
 
181
181
  else:
@@ -189,8 +189,8 @@ else:
189
189
  When `should_flush` is True, ALL of the following run as a single batch. The order matters:
190
190
 
191
191
  ```
192
- 1. Set flushed_through = compacted_through (tentative — see §6.3)
193
- 2. drop_compacted_turns(body, drop_boundary=compacted_through)
192
+ 1. Set flushed_prefix_messages = compacted_prefix_messages (tentative — see §6.3)
193
+ 2. drop_compacted_turns(body, drop_boundary=compacted_prefix_messages)
194
194
  3. collapse_turn_chains(body) [stage 2 — tool chain collapse]
195
195
  4. stub_tool_outputs_by_position(body) [protected-zone intrusion]
196
196
  5. stub_media_by_position(body)
@@ -205,8 +205,8 @@ When the cache is warm and `use_two_pass` is True:
205
205
 
206
206
  ```
207
207
  Pass 1: Build the full outbound payload WITHOUT any historical mutations.
208
- - flushed_through stays at its current value
209
- - All payload-affecting code paths use flushed_through (still 0 or previous flush)
208
+ - flushed_prefix_messages stays at its current value
209
+ - All payload-affecting code paths use flushed_prefix_messages (still 0 or previous flush)
210
210
  - Assembler runs in pre-flush mode (tag-based retrieval, no hints)
211
211
  - No tool/media stubbing, no turn dropping, no chain collapse
212
212
  - Context injection, paging, and assembly run normally
@@ -215,23 +215,23 @@ Pass 1: Build the full outbound payload WITHOUT any historical mutations.
215
215
  Size check: outbound_tokens >= context_window * hard_threshold?
216
216
 
217
217
  Pass 2 (only if size check triggers):
218
- - Set tentative_flush = compacted_through
219
- - Re-run retrieval assembler with flushed_through = tentative_flush
218
+ - Set tentative_flush = compacted_prefix_messages
219
+ - Re-run retrieval assembler with flushed_prefix_messages = tentative_flush
220
220
  (switches to post-flush mode: all summaries, no tag filter)
221
221
  - Apply all flush operations (steps 1–8 above) on body
222
222
  - Re-inject context with post-flush assembled content
223
223
  - Re-inject paging tools
224
224
  - Re-measure outbound_tokens
225
- - Commit: flushed_through = tentative_flush (only after dispatch)
225
+ - Commit: flushed_prefix_messages = tentative_flush (only after dispatch)
226
226
  ```
227
227
 
228
228
  ### Tentative flush and commit
229
229
 
230
- Pass 2 uses a local `tentative_flush` variable. `flushed_through` on EngineState is only updated after the flushed payload is chosen and the upstream request is dispatched. If reassembly or dispatch fails, `flushed_through` stays at its old value and the next request retries. This prevents orphaned state where `flushed_through` advances but no flushed payload was ever served.
230
+ Pass 2 uses a local `tentative_flush` variable. `flushed_prefix_messages` on EngineState is only updated after the flushed payload is chosen and the upstream request is dispatched. If reassembly or dispatch fails, `flushed_prefix_messages` stays at its old value and the next request retries. This prevents orphaned state where `flushed_prefix_messages` advances but no flushed payload was ever served.
231
231
 
232
232
  ### Cold-cache fast path
233
233
 
234
- When `cache_age >= flush_ttl_seconds`, there's no prefix to protect. All flush operations run in a single pass with no reassembly. The assembler is told `flushed_through = compacted_through` from the start.
234
+ When `cache_age >= flush_ttl_seconds`, there's no prefix to protect. All flush operations run in a single pass with no reassembly. The assembler is told `flushed_prefix_messages = compacted_prefix_messages` from the start.
235
235
 
236
236
  ---
237
237
 
@@ -241,7 +241,7 @@ When `cache_age >= flush_ttl_seconds`, there's no prefix to protect. All flush o
241
241
 
242
242
  This requires gating **every** operation that rewrites historical messages:
243
243
 
244
- ### Gated operations (ALL require `flushed_through > previous_flushed_through`)
244
+ ### Gated operations (ALL require `flushed_prefix_messages > previous_flushed_through`)
245
245
 
246
246
  | Operation | Location | What it rewrites |
247
247
  |-----------|----------|-----------------|
@@ -255,11 +255,11 @@ This requires gating **every** operation that rewrites historical messages:
255
255
  | VC tool injection | `tool_query.py:111` | Adds VC tools to tool schema |
256
256
  | `_pre_compaction` flag | `server.py:723` | Selects which stubbing path runs |
257
257
 
258
- **Stage-1 tool stubbing (server.py:780)**: In the current system, this runs on every request regardless of `compacted_through`. When a tool output ages out of the protected window between requests, it gets stubbed, changing a historical message. In deferred mode, this is explicitly gated: stage-1 tool stubbing is **skipped entirely** while the cache is warm. Tool outputs stay raw until flush. This trades payload size for prefix stability — the hard threshold catches the growth.
258
+ **Stage-1 tool stubbing (server.py:780)**: In the current system, this runs on every request regardless of `compacted_prefix_messages`. When a tool output ages out of the protected window between requests, it gets stubbed, changing a historical message. In deferred mode, this is explicitly gated: stage-1 tool stubbing is **skipped entirely** while the cache is warm. Tool outputs stay raw until flush. This trades payload size for prefix stability — the hard threshold catches the growth.
259
259
 
260
260
  **Media stubbing (server.py:796)**: Same treatment. `stub_media_by_position()` is skipped while cache is warm. Images stay inline until flush. Conversations with many large images will hit the hard threshold sooner, triggering an earlier flush.
261
261
 
262
- **`drop_topic_only_stubs()` (server.py:808)**: This removes dead-weight stubs (stubs without restore refs). It only operates on stubs, not raw content. In deferred mode with `flushed_through == 0`, there are no stubs to drop, so it's a no-op. No gating needed.
262
+ **`drop_topic_only_stubs()` (server.py:808)**: This removes dead-weight stubs (stubs without restore refs). It only operates on stubs, not raw content. In deferred mode with `flushed_prefix_messages == 0`, there are no stubs to drop, so it's a no-op. No gating needed.
263
263
 
264
264
  **`merge_consecutive_conversational()` (server.py:813)**: This merges adjacent same-role messages to fix alternation violations. It can change message boundaries. In deferred mode, this should only run on new (unflushed) content, or be skipped if the merge would touch historical messages. **Implementation note**: if the merge only ever touches the last few messages (which is the common case), this is safe. If it can reach into historical messages, gate it.
265
265
 
@@ -286,35 +286,35 @@ This requires gating **every** operation that rewrites historical messages:
286
286
 
287
287
  ---
288
288
 
289
- ## 9. Complete `compacted_through` Audit
289
+ ## 9. Complete `compacted_prefix_messages` Audit
290
290
 
291
- Every code path that reads `compacted_through` is classified:
291
+ Every code path that reads `compacted_prefix_messages` is classified:
292
292
 
293
- ### Must use `flushed_through` (payload-affecting paths)
293
+ ### Must use `flushed_prefix_messages` (payload-affecting paths)
294
294
 
295
295
  | Location | Current code | Why |
296
296
  |----------|-------------|-----|
297
- | `retrieval_assembler.py:112` | `if compacted_through > 0: active_tags = []` | Suppresses tag retrieval; only after raw turns are gone |
298
- | `retrieval_assembler.py:141` | `post_compaction=(compacted_through > 0)` | Enables summary-floor injection in retriever |
299
- | `retrieval_assembler.py:199` | `post_compaction=(compacted_through > 0)` | Same (retry path) |
300
- | `retrieval_assembler.py:336` | `watermark = compacted_through` | Turn filtering in `_filter_irrelevant_turns()` |
301
- | `retrieval_assembler.py:419` | `if compacted_through == 0: return ""` | Gates `_build_context_hint()` emission |
302
- | `server.py:685` | `if compacted_through > 0: drop_compacted_turns(...)` | Gated by flush (§6) |
303
- | `server.py:723` | `_pre_compaction = compacted_through == 0` | Selects stage-1 vs stage-2 stubbing path |
304
- | `server.py:744` | `_ct = int(compacted_through)` | Post-compaction chain collapse |
305
- | `server.py:831` | `compacted_count = int(compacted_through)` | Paging tool injection |
306
- | `tool_query.py:111` | `compacted_through > 0` | VC tool injection |
297
+ | `retrieval_assembler.py:112` | `if compacted_prefix_messages > 0: active_tags = []` | Suppresses tag retrieval; only after raw turns are gone |
298
+ | `retrieval_assembler.py:141` | `post_compaction=(compacted_prefix_messages > 0)` | Enables summary-floor injection in retriever |
299
+ | `retrieval_assembler.py:199` | `post_compaction=(compacted_prefix_messages > 0)` | Same (retry path) |
300
+ | `retrieval_assembler.py:336` | `watermark = compacted_prefix_messages` | Turn filtering in `_filter_irrelevant_turns()` |
301
+ | `retrieval_assembler.py:419` | `if compacted_prefix_messages == 0: return ""` | Gates `_build_context_hint()` emission |
302
+ | `server.py:685` | `if compacted_prefix_messages > 0: drop_compacted_turns(...)` | Gated by flush (§6) |
303
+ | `server.py:723` | `_pre_compaction = compacted_prefix_messages == 0` | Selects stage-1 vs stage-2 stubbing path |
304
+ | `server.py:744` | `_ct = int(compacted_prefix_messages)` | Post-compaction chain collapse |
305
+ | `server.py:831` | `compacted_count = int(compacted_prefix_messages)` | Paging tool injection |
306
+ | `tool_query.py:111` | `compacted_prefix_messages > 0` | VC tool injection |
307
307
 
308
308
  ### Must be parameterized (`history_offset`)
309
309
 
310
310
  | Location | Current code | Fix |
311
311
  |----------|-------------|-----|
312
- | `types.py:317` | `history_offset()` returns `compacted_through` | Add `watermark` parameter (see §9.1) |
313
- | `retrieval_assembler.py:119,274,359` | Callers of `history_offset()` | Pass `watermark=flushed_through` |
314
- | `compaction_pipeline.py:128,179` | Callers of `history_offset()` | Pass no watermark (default = `compacted_through`) |
315
- | `tagging_pipeline.py:337` | Caller of `history_offset()` | Pass no watermark (default = `compacted_through`) |
312
+ | `types.py:317` | `history_offset()` returns `compacted_prefix_messages` | Add `watermark` parameter (see §9.1) |
313
+ | `retrieval_assembler.py:119,274,359` | Callers of `history_offset()` | Pass `watermark=flushed_prefix_messages` |
314
+ | `compaction_pipeline.py:128,179` | Callers of `history_offset()` | Pass no watermark (default = `compacted_prefix_messages`) |
315
+ | `tagging_pipeline.py:337` | Caller of `history_offset()` | Pass no watermark (default = `compacted_prefix_messages`) |
316
316
 
317
- ### Keep `compacted_through` (compaction/storage paths)
317
+ ### Keep `compacted_prefix_messages` (compaction/storage paths)
318
318
 
319
319
  | Location | Why it stays |
320
320
  |----------|-------------|
@@ -324,46 +324,46 @@ Every code path that reads `compacted_through` is classified:
324
324
  | `engine.py:892,979,981,1036,1089` | Snapshot save/diagnostic |
325
325
  | `engine.py:1235,1236,1301,1304,1329,1334` | Checkpoint comparison |
326
326
 
327
- ### Informational (add `flushed_through` alongside)
327
+ ### Informational (add `flushed_prefix_messages` alongside)
328
328
 
329
329
  | Location | Change |
330
330
  |----------|--------|
331
331
  | `dashboard.py:272,522,739,1098` | Display both watermarks |
332
332
  | `helpers.py:510` | Log both watermarks |
333
333
  | `state.py:473,574,796` | Log both watermarks |
334
- | `dashboard.html:615,789,1142,1144,1178,1399,1826` | Show `flushed_through` indicator |
334
+ | `dashboard.html:615,789,1142,1144,1178,1399,1826` | Show `flushed_prefix_messages` indicator |
335
335
 
336
336
  ### 9.1 `history_offset()` Parameterization
337
337
 
338
- `history_offset()` (`types.py:317`) is shared by compaction/tagging (need `compacted_through`) and payload-assembly (need `flushed_through`). A blanket swap breaks compaction.
338
+ `history_offset()` (`types.py:317`) is shared by compaction/tagging (need `compacted_prefix_messages`) and payload-assembly (need `flushed_prefix_messages`). A blanket swap breaks compaction.
339
339
 
340
340
  **Fix**: Add an optional `watermark` parameter:
341
341
 
342
342
  ```python
343
343
  def history_offset(self, history_len: int, *, total_turns_indexed: int | None = None,
344
344
  watermark: int | None = None) -> int:
345
- wm = watermark if watermark is not None else self.compacted_through
345
+ wm = watermark if watermark is not None else self.compacted_prefix_messages
346
346
  if wm < history_len:
347
347
  return wm
348
348
  # ... rest of sliding-window logic uses wm
349
349
  ```
350
350
 
351
- - Compaction/tagging callers: no `watermark` arg (defaults to `compacted_through`)
352
- - Retrieval assembler callers: `watermark=self._engine_state.flushed_through`
351
+ - Compaction/tagging callers: no `watermark` arg (defaults to `compacted_prefix_messages`)
352
+ - Retrieval assembler callers: `watermark=self._engine_state.flushed_prefix_messages`
353
353
 
354
354
  ---
355
355
 
356
356
  ## 10. `drop_compacted_turns()` Boundary Fix
357
357
 
358
- **Current behavior** (`message_filter.py:681–753`): Uses `compacted_through` as a gate only (`> 0` → enabled). Once enabled, drops ALL non-tool turns outside the protected window via `range(protected_start)`, regardless of whether those turns have summaries.
358
+ **Current behavior** (`message_filter.py:681–753`): Uses `compacted_prefix_messages` as a gate only (`> 0` → enabled). Once enabled, drops ALL non-tool turns outside the protected window via `range(protected_start)`, regardless of whether those turns have summaries.
359
359
 
360
- **Problem**: When flush fires after multiple deferred prepare rounds, `compacted_through` might be at turn 50 but 50 more turns have accumulated. Protected window covers the last 6. The function drops turns 0–94, but only 0–49 have summaries.
360
+ **Problem**: When flush fires after multiple deferred prepare rounds, `compacted_prefix_messages` might be at turn 50 but 50 more turns have accumulated. Protected window covers the last 6. The function drops turns 0–94, but only 0–49 have summaries.
361
361
 
362
362
  **Fix**: Add `drop_boundary` parameter. Only drop turns below the boundary:
363
363
 
364
364
  ```python
365
365
  def drop_compacted_turns(
366
- body, turn_tag_index, compacted_through,
366
+ body, turn_tag_index, compacted_prefix_messages,
367
367
  *, fmt=None, protected_recent_turns=6,
368
368
  drop_boundary: int | None = None, # NEW
369
369
  ) -> tuple[dict, int]:
@@ -374,35 +374,35 @@ def drop_compacted_turns(
374
374
  # ... existing drop logic
375
375
  ```
376
376
 
377
- Flush gate calls: `drop_compacted_turns(body, ..., drop_boundary=compacted_through)`
377
+ Flush gate calls: `drop_compacted_turns(body, ..., drop_boundary=compacted_prefix_messages)`
378
378
 
379
379
  ---
380
380
 
381
381
  ## 11. Complete Persistence Surface
382
382
 
383
- Both `flushed_through` and `last_request_time` must be persisted across ALL serialization paths.
383
+ Both `flushed_prefix_messages` and `last_request_time` must be persisted across ALL serialization paths.
384
384
 
385
385
  ### Write-side (SAVE)
386
386
 
387
387
  | Location | What to add |
388
388
  |----------|------------|
389
- | `engine.py:891` (`extract_session_state()`) | `flushed_through=self._engine_state.flushed_through, last_request_time=self._engine_state.last_request_time` |
389
+ | `engine.py:891` (`extract_session_state()`) | `flushed_prefix_messages=self._engine_state.flushed_prefix_messages, last_request_time=self._engine_state.last_request_time` |
390
390
  | `engine.py:979` (`EngineStateSnapshot(...)` constructor) | Same two fields |
391
391
 
392
392
  ### Dataclasses
393
393
 
394
394
  | Location | What to add |
395
395
  |----------|------------|
396
- | `types.py` EngineState | `flushed_through: int = 0`, `last_request_time: float = 0.0` |
397
- | `types.py` EngineStateSnapshot | `flushed_through: int = 0`, `last_request_time: float = 0.0` |
398
- | `session_state.py` SessionState | `flushed_through: int = 0`, `last_request_time: float = 0.0` |
396
+ | `types.py` EngineState | `flushed_prefix_messages: int = 0`, `last_request_time: float = 0.0` |
397
+ | `types.py` EngineStateSnapshot | `flushed_prefix_messages: int = 0`, `last_request_time: float = 0.0` |
398
+ | `session_state.py` SessionState | `flushed_prefix_messages: int = 0`, `last_request_time: float = 0.0` |
399
399
 
400
400
  ### Serialization (SessionState)
401
401
 
402
402
  | Location | What to add |
403
403
  |----------|------------|
404
404
  | `session_state.py:39–58` (`to_json()`) | Include both fields in dict |
405
- | `session_state.py:60–80` (`from_json()`) | Read with `.get("flushed_through", 0)` etc. |
405
+ | `session_state.py:60–80` (`from_json()`) | Read with `.get("flushed_prefix_messages", 0)` etc. |
406
406
 
407
407
  ### Conversion (SessionState ↔ EngineStateSnapshot)
408
408
 
@@ -415,9 +415,9 @@ Both `flushed_through` and `last_request_time` must be persisted across ALL seri
415
415
 
416
416
  | Location | What to add |
417
417
  |----------|------------|
418
- | `engine.py:554` | `self._engine_state.flushed_through = saved.flushed_through` |
419
- | `engine.py:637` | `self._engine_state.flushed_through = es.get("flushed_through", 0)` |
420
- | `engine.py:773` | `self._engine_state.flushed_through = state.flushed_through` |
418
+ | `engine.py:554` | `self._engine_state.flushed_prefix_messages = saved.flushed_prefix_messages` |
419
+ | `engine.py:637` | `self._engine_state.flushed_prefix_messages = es.get("flushed_prefix_messages", 0)` |
420
+ | `engine.py:773` | `self._engine_state.flushed_prefix_messages = state.flushed_prefix_messages` |
421
421
  | Same three lines | Parallel for `last_request_time` |
422
422
 
423
423
  ### Storage backends
@@ -426,7 +426,7 @@ Both `flushed_through` and `last_request_time` must be persisted across ALL seri
426
426
  |----------|--------|
427
427
  | `filesystem.py:677` (save) | Add both fields to dict |
428
428
  | `filesystem.py:737` (load) | Read with defaults |
429
- | `sqlite.py:75` (schema) | `ALTER TABLE engine_state ADD COLUMN flushed_through INTEGER NOT NULL DEFAULT 0; ADD COLUMN last_request_time REAL NOT NULL DEFAULT 0.0;` |
429
+ | `sqlite.py:75` (schema) | `ALTER TABLE engine_state ADD COLUMN flushed_prefix_messages INTEGER NOT NULL DEFAULT 0; ADD COLUMN last_request_time REAL NOT NULL DEFAULT 0.0;` |
430
430
  | `sqlite.py:1582` (insert) | Include new columns |
431
431
  | `sqlite.py:1608,1628,1664` (load) | Read new columns |
432
432
  | `postgres.py:85` (schema) | Same DDL |
@@ -436,7 +436,7 @@ Both `flushed_through` and `last_request_time` must be persisted across ALL seri
436
436
  ### Backward compatibility on deserialize
437
437
 
438
438
  All new fields default to 0/0.0. Old state without these fields deserializes correctly. On first request after upgrade:
439
- - `flushed_through = 0` + `compacted_through > 0` → pre-flush mode
439
+ - `flushed_prefix_messages = 0` + `compacted_prefix_messages > 0` → pre-flush mode
440
440
  - `last_request_time = 0.0` → `cache_age` huge → cold-cache fast path → immediate flush
441
441
 
442
442
  First request flushes pending compaction, then subsequent requests benefit from deferral.
@@ -448,13 +448,13 @@ First request flushes pending compaction, then subsequent requests benefit from
448
448
  ### Defer event
449
449
 
450
450
  ```
451
- DEFER-PAYLOAD: cache_age=%.1fs outbound_tokens=%d threshold=%d compacted_through=%d flushed_through=%d — preserving prefix
451
+ DEFER-PAYLOAD: cache_age=%.1fs outbound_tokens=%d threshold=%d compacted_prefix_messages=%d flushed_prefix_messages=%d — preserving prefix
452
452
  ```
453
453
 
454
454
  ### Flush event
455
455
 
456
456
  ```
457
- FLUSH-PAYLOAD: reason=%s cache_age=%.1fs outbound_tokens=%d compacted_through=%d flushed_through=%d→%d turns_dropped=%d tools_stubbed=%d media_stubbed=%d
457
+ FLUSH-PAYLOAD: reason=%s cache_age=%.1fs outbound_tokens=%d compacted_prefix_messages=%d flushed_prefix_messages=%d→%d turns_dropped=%d tools_stubbed=%d media_stubbed=%d
458
458
  ```
459
459
 
460
460
  Where `reason` is one of: `cold_cache`, `size_threshold`, `legacy_immediate`.
@@ -492,13 +492,13 @@ This helper at `server.py:813` merges adjacent same-role messages. If a merge re
492
492
  Pass 1 measures full payload. Exceeds threshold. Pass 2 flushes and reassembles. Subsequent requests benefit from deferral.
493
493
 
494
494
  ### Multiple prepare rounds pending
495
- Prepare runs several times advancing `compacted_through` from N to M to P. `flushed_through` stays at 0. Retrieval assembler stays in pre-flush mode (no duplicate content). On flush, `flushed_through` jumps to P. `drop_compacted_turns(drop_boundary=P)` drops only summarized turns.
495
+ Prepare runs several times advancing `compacted_prefix_messages` from N to M to P. `flushed_prefix_messages` stays at 0. Retrieval assembler stays in pre-flush mode (no duplicate content). On flush, `flushed_prefix_messages` jumps to P. `drop_compacted_turns(drop_boundary=P)` drops only summarized turns.
496
496
 
497
497
  ### Slow conversations (>5 min between turns)
498
498
  Every request finds `cache_age >= flush_ttl_seconds`. Cold-cache fast path. Behaves like today. No deferral benefit (correct: cache already expired).
499
499
 
500
- ### `flushed_through > compacted_through` guard
501
- Assert invariant on every update. If violated, log error and clamp: `flushed_through = min(flushed_through, compacted_through)`.
500
+ ### `flushed_prefix_messages > compacted_prefix_messages` guard
501
+ Assert invariant on every update. If violated, log error and clamp: `flushed_prefix_messages = min(flushed_prefix_messages, compacted_prefix_messages)`.
502
502
 
503
503
  ### Cross-worker handoff
504
504
  Both watermarks persisted in Redis checkpoint and store backends. New worker restores correct state. If checkpoint is lost, defaults to 0/0.0 → cold-cache flush on first request.
@@ -507,7 +507,7 @@ Both watermarks persisted in Redis checkpoint and store backends. New worker res
507
507
  Warm-cache-near-limit path pays for two assembly cycles. Fires once per flush cycle (when payload first hits hard threshold). For 200k window with soft=0.30 and hard=0.97, the payload grows from ~60k to ~194k over many turns before this triggers. Expected: rare, bounded cost.
508
508
 
509
509
  ### `defer_payload_mutation=False` with new fields
510
- `flushed_through` is set to `compacted_through` on every request (auto-tracking). All existing code paths run unchanged. The new fields exist but are no-ops. Zero behavioral change.
510
+ `flushed_prefix_messages` is set to `compacted_prefix_messages` on every request (auto-tracking). All existing code paths run unchanged. The new fields exist but are no-ops. Zero behavioral change.
511
511
 
512
512
  ---
513
513
 
@@ -517,21 +517,21 @@ Warm-cache-near-limit path pays for two assembly cycles. Fires once per flush cy
517
517
 
518
518
  | Test | Verifies |
519
519
  |------|----------|
520
- | `test_defer_off_identical_to_today` | `defer_payload_mutation=False` → exact same behavior, `flushed_through` auto-tracks `compacted_through` |
521
- | `test_prepare_does_not_mutate_body` | Prepare phase advances `compacted_through` but body is unchanged |
520
+ | `test_defer_off_identical_to_today` | `defer_payload_mutation=False` → exact same behavior, `flushed_prefix_messages` auto-tracks `compacted_prefix_messages` |
521
+ | `test_prepare_does_not_mutate_body` | Prepare phase advances `compacted_prefix_messages` but body is unchanged |
522
522
  | `test_warm_cache_skips_all_mutations` | Cache warm + below threshold → no turn drops, no tool stubs, no media stubs, no chain collapse |
523
- | `test_flush_at_hard_threshold` | Warm cache + outbound >= threshold → all mutations applied, `flushed_through` advanced |
523
+ | `test_flush_at_hard_threshold` | Warm cache + outbound >= threshold → all mutations applied, `flushed_prefix_messages` advanced |
524
524
  | `test_flush_when_cache_cold` | `cache_age >= TTL` → cold-cache fast path, all mutations applied |
525
525
  | `test_stage1_tool_stubbing_gated` | Warm cache → stage-1 `stub_tool_outputs_by_position()` is skipped (tool outputs stay raw) |
526
526
  | `test_media_stubbing_gated` | Warm cache → `stub_media_by_position()` is skipped (images stay inline) |
527
527
  | `test_chain_collapse_gated` | Warm cache → `collapse_turn_chains()` is skipped |
528
- | `test_assembler_uses_flushed_through` | `compacted_through=10, flushed_through=0` → pre-flush mode (tag retrieval, no hints) |
529
- | `test_assembler_post_flush` | `compacted_through=10, flushed_through=10` → post-flush mode (all summaries, hints) |
528
+ | `test_assembler_uses_flushed_through` | `compacted_prefix_messages=10, flushed_prefix_messages=0` → pre-flush mode (tag retrieval, no hints) |
529
+ | `test_assembler_post_flush` | `compacted_prefix_messages=10, flushed_prefix_messages=10` → post-flush mode (all summaries, hints) |
530
530
  | `test_drop_respects_boundary` | `drop_boundary=50` with 100 turns → only turns below boundary dropped |
531
- | `test_history_offset_parameterized` | Assembler callers pass `flushed_through`; compaction callers use default `compacted_through` |
531
+ | `test_history_offset_parameterized` | Assembler callers pass `flushed_prefix_messages`; compaction callers use default `compacted_prefix_messages` |
532
532
  | `test_timestamp_set_after_dispatch` | `last_request_time` updated after upstream call, not before gate |
533
- | `test_tentative_flush_not_committed_on_failure` | If pass-2 dispatch fails, `flushed_through` stays at old value |
534
- | `test_invariant_enforcement` | Setting `flushed_through > compacted_through` → error log + clamp |
533
+ | `test_tentative_flush_not_committed_on_failure` | If pass-2 dispatch fails, `flushed_prefix_messages` stays at old value |
534
+ | `test_invariant_enforcement` | Setting `flushed_prefix_messages > compacted_prefix_messages` → error log + clamp |
535
535
  | `test_persistence_roundtrip` | Save/restore round-trips both watermarks through all paths: SessionState, EngineStateSnapshot, filesystem, sqlite, postgres |
536
536
  | `test_two_pass_reassembles_context` | Pass 2 re-runs assembler in post-flush mode with correct summaries |
537
537
 
@@ -539,10 +539,10 @@ Warm-cache-near-limit path pays for two assembly cycles. Fires once per flush cy
539
539
 
540
540
  1. Tenant with `defer_payload_mutation=true, soft_threshold=0.30, hard_threshold=0.97`
541
541
  2. Send requests until prepare fires at soft threshold
542
- 3. Verify: `compacted_through` advanced, `flushed_through=0`, payload byte-identical to previous request (excluding new turns)
542
+ 3. Verify: `compacted_prefix_messages` advanced, `flushed_prefix_messages=0`, payload byte-identical to previous request (excluding new turns)
543
543
  4. Verify: no tool stubs, no media stubs, no chain collapse, no context hints
544
544
  5. Continue sending until fully-assembled payload hits 97%
545
- 6. Verify: pass 2 fires. All mutations applied atomically. `flushed_through = compacted_through`. Payload shrinks.
545
+ 6. Verify: pass 2 fires. All mutations applied atomically. `flushed_prefix_messages = compacted_prefix_messages`. Payload shrinks.
546
546
  7. Verify: metrics report from pass 2 (not pass 1)
547
547
 
548
548
  ### Production validation
@@ -559,15 +559,15 @@ Warm-cache-near-limit path pays for two assembly cycles. Fires once per flush cy
559
559
 
560
560
  | File | Change |
561
561
  |------|--------|
562
- | `virtual_context/types.py` | Add `defer_payload_mutation`, `flush_ttl_seconds` to MonitorConfig. Add `flushed_through`, `last_request_time` to EngineState and EngineStateSnapshot. Add `watermark` param to `history_offset()`. |
562
+ | `virtual_context/types.py` | Add `defer_payload_mutation`, `flush_ttl_seconds` to MonitorConfig. Add `flushed_prefix_messages`, `last_request_time` to EngineState and EngineStateSnapshot. Add `watermark` param to `history_offset()`. |
563
563
  | `virtual_context/proxy/session_state.py` | Add fields to SessionState, `to_json()`, `from_json()`, `_state_to_snapshot()`, `_snapshot_to_state()` |
564
564
  | `virtual_context/engine.py` | Write-side: map new fields in `extract_session_state()` (line 891) and `EngineStateSnapshot()` constructor (line 979). Load-side: restore at lines 554, 637, 773. |
565
- | `virtual_context/core/retrieval_assembler.py` | Lines 112, 141, 199, 336, 419: switch to `flushed_through`. Lines 119, 274, 359: pass `watermark=flushed_through` to `history_offset()`. |
566
- | `virtual_context/core/tool_query.py` | Line 111: switch to `flushed_through` |
565
+ | `virtual_context/core/retrieval_assembler.py` | Lines 112, 141, 199, 336, 419: switch to `flushed_prefix_messages`. Lines 119, 274, 359: pass `watermark=flushed_prefix_messages` to `history_offset()`. |
566
+ | `virtual_context/core/tool_query.py` | Line 111: switch to `flushed_prefix_messages` |
567
567
  | `virtual_context/proxy/server.py` | Flush gate with two-pass logic. Gate ALL mutation operations (drop, stub, collapse, media, stage-1). Set `last_request_time` after dispatch. Tentative flush commit. Logging. Legacy auto-tracking when `defer_payload_mutation=False`. |
568
568
  | `virtual_context/proxy/message_filter.py` | Add `drop_boundary` param to `drop_compacted_turns()` |
569
569
  | `virtual_context/storage/filesystem.py` | Serialize/deserialize new fields |
570
570
  | `virtual_context/storage/sqlite.py` | Schema DDL, insert, load paths |
571
571
  | `virtual_context/storage/postgres.py` | Schema DDL, upsert, load paths |
572
- | `virtual_context/proxy/dashboard.py` | Expose `flushed_through` alongside `compacted_through` |
573
- | `virtual_context/proxy/helpers.py` | Log `flushed_through` |
572
+ | `virtual_context/proxy/dashboard.py` | Expose `flushed_prefix_messages` alongside `compacted_prefix_messages` |
573
+ | `virtual_context/proxy/helpers.py` | Log `flushed_prefix_messages` |
@@ -94,8 +94,8 @@ class TestCompactionCycle:
94
94
  history = _simulate_turns(engine, pairs)
95
95
 
96
96
  # Compaction should have fired
97
- assert engine._engine_state.compacted_through > 0, (
98
- f"Expected compaction, but _compacted_through={engine._engine_state.compacted_through}"
97
+ assert engine._engine_state.compacted_prefix_messages > 0, (
98
+ f"Expected compaction, but _compacted_through={engine._engine_state.compacted_prefix_messages}"
99
99
  )
100
100
 
101
101
  # Post-compaction retrieval should still work
@@ -204,7 +204,7 @@ class TestCompactionCycle:
204
204
  history = _simulate_turns(engine, pairs)
205
205
 
206
206
  # Should have compacted multiple times
207
- assert engine._engine_state.compacted_through > 0
207
+ assert engine._engine_state.compacted_prefix_messages > 0
208
208
 
209
209
  # Engine should still respond without errors
210
210
  result = engine.on_message_inbound("What topics did we cover?", history)