python-delphi-lsp 2.1.0__tar.gz → 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. {python_delphi_lsp-2.1.0/python_delphi_lsp.egg-info → python_delphi_lsp-2.2.0}/PKG-INFO +21 -11
  2. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/README.md +19 -10
  3. python_delphi_lsp-2.2.0/delphi_lsp/_version.py +1 -0
  4. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/agent_cache.py +46 -6
  5. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/agent_context.py +323 -33
  6. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/agent_relations.py +28 -3
  7. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/agent_workspace.py +35 -0
  8. python_delphi_lsp-2.2.0/delphi_lsp/delphiast_lexer.py +284 -0
  9. python_delphi_lsp-2.2.0/delphi_lsp/delphiast_parser.py +1498 -0
  10. python_delphi_lsp-2.2.0/delphi_lsp/delphiast_tokens.py +41 -0
  11. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/metrics.py +35 -1
  12. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/parallel_outline.py +49 -10
  13. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/parser.py +54 -4
  14. python_delphi_lsp-2.2.0/delphi_lsp/parser_backend.py +57 -0
  15. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/project_indexer.py +32 -1
  16. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/pyproject.toml +2 -1
  17. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0/python_delphi_lsp.egg-info}/PKG-INFO +21 -11
  18. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/python_delphi_lsp.egg-info/SOURCES.txt +9 -0
  19. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/python_delphi_lsp.egg-info/requires.txt +1 -0
  20. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_agent_cache.py +104 -6
  21. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_agent_context.py +137 -1
  22. python_delphi_lsp-2.2.0/tests/test_delphiast_lexer.py +97 -0
  23. python_delphi_lsp-2.2.0/tests/test_delphiast_native_compatibility.py +364 -0
  24. python_delphi_lsp-2.2.0/tests/test_delphiast_parser.py +121 -0
  25. python_delphi_lsp-2.2.0/tests/test_delphiast_semantic_compatibility.py +74 -0
  26. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_package_metadata.py +3 -3
  27. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_parallel_outline.py +31 -3
  28. python_delphi_lsp-2.2.0/tests/test_parser_backends.py +135 -0
  29. python_delphi_lsp-2.1.0/delphi_lsp/_version.py +0 -1
  30. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/.github/workflows/ci.yml +0 -0
  31. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/LICENSE +0 -0
  32. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/MANIFEST.in +0 -0
  33. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/__init__.py +0 -0
  34. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/agent_cli.py +0 -0
  35. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/agent_layers.py +0 -0
  36. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/agent_metrics.py +0 -0
  37. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/agent_protocol.py +0 -0
  38. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/agent_templates.py +0 -0
  39. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/binary.py +0 -0
  40. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/comment_builder.py +0 -0
  41. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/consts.py +0 -0
  42. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/grammar.py +0 -0
  43. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/lark_builder.py +0 -0
  44. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/lark_tokens.py +0 -0
  45. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/lsp_server.py +0 -0
  46. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/nodes.py +0 -0
  47. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/preprocessor.py +0 -0
  48. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/progress.py +0 -0
  49. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/project_discovery.py +0 -0
  50. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/semantic.py +0 -0
  51. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/semantic_builder.py +0 -0
  52. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/source_reader.py +0 -0
  53. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/workspace.py +0 -0
  54. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/delphi_lsp/writer.py +0 -0
  55. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/opencode.json +0 -0
  56. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/python_delphi_lsp.egg-info/dependency_links.txt +0 -0
  57. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/python_delphi_lsp.egg-info/entry_points.txt +0 -0
  58. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/python_delphi_lsp.egg-info/top_level.txt +0 -0
  59. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/audit_delphi_language_features.py +0 -0
  60. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/benchmark_github_corpus.py +0 -0
  61. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/benchmark_parallel_cache.py +0 -0
  62. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/bootstrap_vllm_codebase_skill_test.py +0 -0
  63. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/bootstrap_vllm_opencode_test.ps1 +0 -0
  64. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/bootstrap_vllm_opencode_test.py +0 -0
  65. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/bootstrap_vllm_opencode_test.sh +0 -0
  66. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/build_github_performance_corpus.py +0 -0
  67. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/check_ornith_cache.py +0 -0
  68. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/generate_progress_pdf.py +0 -0
  69. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/generate_release_evidence.py +0 -0
  70. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/ollama/ornith-lspctx.Modelfile +0 -0
  71. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/prepare_ornith_cache.py +0 -0
  72. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/run_opencode_lsp_probe.py +0 -0
  73. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/run_openrouter_github_e2e.py +0 -0
  74. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/start_ornith_vllm.sh +0 -0
  75. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/scripts/summarize_opencode_request_payloads.py +0 -0
  76. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/setup.cfg +0 -0
  77. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/corpora.performance.lock.json +0 -0
  78. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/DeprecatedOnConst.pas +0 -0
  79. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/VariantRecordFieldAttributes.pas +0 -0
  80. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/alignedrecords.pas +0 -0
  81. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/constset.pas +0 -0
  82. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/deprecatedtype.pas +0 -0
  83. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/dottedtypes.pas +0 -0
  84. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/endtoken.pas +0 -0
  85. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/experimentals.pas +0 -0
  86. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/externalfunction.pas +0 -0
  87. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/finalizationinitializationexports.pas +0 -0
  88. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/forwardoverloaded.pas +0 -0
  89. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/forwardwithoutsemicolon.pas +0 -0
  90. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/genericconstraints.pas +0 -0
  91. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/genericinterfacemethoddelegation.pas +0 -0
  92. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/implementsgenerictype.pas +0 -0
  93. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/include file2.inc +0 -0
  94. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/includefile.inc +0 -0
  95. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/includefile.pas +0 -0
  96. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/managedrecords.pas +0 -0
  97. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/messagemethod.pas +0 -0
  98. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/multiline.pas +0 -0
  99. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/nonalignedrecords.pas +0 -0
  100. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/numbers.pas +0 -0
  101. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/pointerchars.pas +0 -0
  102. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/properties.pas +0 -0
  103. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/strictvisibility.pas +0 -0
  104. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/tryexcept.pas +0 -0
  105. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/umlauts.pas +0 -0
  106. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/legacy_snippets/whitespacearoundifdefcondition.pas +0 -0
  107. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/library_demo.dpr +0 -0
  108. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/package_demo.dpk +0 -0
  109. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/program_demo.dpr +0 -0
  110. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_advanced.pas +0 -0
  111. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_attributes.pas +0 -0
  112. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_basic.pas +0 -0
  113. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_consumer.pas +0 -0
  114. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_generics.pas +0 -0
  115. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_inheritance.pas +0 -0
  116. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_math.pas +0 -0
  117. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_properties.pas +0 -0
  118. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_sections.pas +0 -0
  119. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_statements.pas +0 -0
  120. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_types.pas +0 -0
  121. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_unresolved.pas +0 -0
  122. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/fixtures/unit_with.pas +0 -0
  123. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_agent_codebase.py +0 -0
  124. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_agent_metrics.py +0 -0
  125. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_agent_protocol.py +0 -0
  126. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_agent_relations.py +0 -0
  127. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_agent_worker.py +0 -0
  128. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_agent_workspace.py +0 -0
  129. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_bootstrap_vllm_codebase_skill.py +0 -0
  130. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_bootstrap_vllm_opencode.py +0 -0
  131. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_diagnostics.py +0 -0
  132. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_github_performance_corpus.py +0 -0
  133. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_language_feature_matrix.py +0 -0
  134. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_legacy_snippets.py +0 -0
  135. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_lsp_features.py +0 -0
  136. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_lsp_support.py +0 -0
  137. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_metrics.py +0 -0
  138. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_opencode_config.py +0 -0
  139. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_opencode_probe.py +0 -0
  140. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_opencode_request_payloads.py +0 -0
  141. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_openrouter_github_e2e.py +0 -0
  142. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_ornith_cache_prepare.py +0 -0
  143. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_parser.py +0 -0
  144. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_preprocessor.py +0 -0
  145. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_progress.py +0 -0
  146. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_project_discovery.py +0 -0
  147. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_project_indexer.py +0 -0
  148. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_release_evidence.py +0 -0
  149. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_semantic.py +0 -0
  150. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_vllm_start_script.py +0 -0
  151. {python_delphi_lsp-2.1.0 → python_delphi_lsp-2.2.0}/tests/test_workspace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-delphi-lsp
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Python Delphi/Object Pascal parser, semantic indexer, and language server.
5
5
  Author: Dark Light
6
6
  License-Expression: MPL-2.0
@@ -24,6 +24,7 @@ License-File: LICENSE
24
24
  Requires-Dist: lark>=1.1.9
25
25
  Requires-Dist: pygls<2.0,>=1.3.0
26
26
  Requires-Dist: lsprotocol>=2023.0.1
27
+ Requires-Dist: watchfiles<2.0,>=1.2
27
28
  Provides-Extra: lsp
28
29
  Requires-Dist: pygls<2.0,>=1.3.0; extra == "lsp"
29
30
  Requires-Dist: lsprotocol>=2023.0.1; extra == "lsp"
@@ -41,7 +42,7 @@ Dynamic: license-file
41
42
 
42
43
  `python-delphi-lsp` parses Delphi/Object Pascal, builds semantic and project
43
44
  indexes, serves LSP, and provides bounded codebase navigation for agents.
44
- Version 2.1.0 is authored by Dark Light and supports Windows, macOS, and Linux.
45
+ Version 2.2.0 is authored by Dark Light and supports Windows, macOS, and Linux.
45
46
 
46
47
  ## Install and quick start
47
48
 
@@ -238,19 +239,28 @@ above 80 percent.
238
239
 
239
240
  Cold builds parse independent source units in short-lived processes created with
240
241
  the cross-platform `spawn` method. `--workers auto|N` defaults to `auto`.
241
- Automatic selection uses at most four worker processes, leaves one detected CPU
242
+ Automatic selection uses at most eight worker processes, leaves one detected CPU
242
243
  free, never exceeds the source task count, and—for the cache daemon—allows one
243
- worker per `128 MiB` of retained-cache budget. `view` and `index` use the same
244
- task, CPU, and four-worker caps without the cache-budget term. An explicit value
244
+ worker per `64 MiB` of retained-cache budget. `view` and `index` use the same
245
+ task, CPU, and eight-worker caps without the cache-budget term. An explicit value
245
246
  from 1 through 32 overrides the automatic CPU and memory caps but is still
246
247
  limited by the number of tasks.
247
248
 
248
- Worker processes exit before retained-cache accounting. Their models and source
249
- text are streamed into the parent, so transient worker memory is separate from
250
- the retained navigation structures and the existing 80-percent warning. If an
251
- automatic pool fails before accepting a result, one automatic serial fallback
252
- is attempted; explicit worker counts fail instead of silently changing the
253
- requested configuration.
249
+ Worker processes build compact, detached navigation shards and exit before
250
+ retained-cache accounting. Full semantic object graphs and source text are not
251
+ retained in the parent. Tokenized source documents are loaded only for source
252
+ evidence requests such as `inspect`, and a byte-bounded LRU evicts older
253
+ documents. The transient worker memory remains separate from retained navigation
254
+ structures and the existing 80-percent warning. Retained bytes are maintained
255
+ incrementally; requests never traverse the complete cache object graph merely
256
+ to measure it. If an automatic pool fails before accepting a result, one
257
+ automatic serial fallback is attempted; explicit worker counts fail instead of
258
+ silently changing the requested configuration.
259
+
260
+ Cache prewarming builds the navigation registry directly without constructing
261
+ an empty-query result, symbol cards, pagination, or JSON payloads. Up to sixteen
262
+ recent ranked queries are retained in a small LRU so alternating CLI and
263
+ OpenCode searches remain warm.
254
264
 
255
265
  `cache start` waits up to `--startup-timeout 120` seconds by default for a large
256
266
  workspace to prewarm. The timeout belongs to the starting client and does not
@@ -2,7 +2,7 @@
2
2
 
3
3
  `python-delphi-lsp` parses Delphi/Object Pascal, builds semantic and project
4
4
  indexes, serves LSP, and provides bounded codebase navigation for agents.
5
- Version 2.1.0 is authored by Dark Light and supports Windows, macOS, and Linux.
5
+ Version 2.2.0 is authored by Dark Light and supports Windows, macOS, and Linux.
6
6
 
7
7
  ## Install and quick start
8
8
 
@@ -199,19 +199,28 @@ above 80 percent.
199
199
 
200
200
  Cold builds parse independent source units in short-lived processes created with
201
201
  the cross-platform `spawn` method. `--workers auto|N` defaults to `auto`.
202
- Automatic selection uses at most four worker processes, leaves one detected CPU
202
+ Automatic selection uses at most eight worker processes, leaves one detected CPU
203
203
  free, never exceeds the source task count, and—for the cache daemon—allows one
204
- worker per `128 MiB` of retained-cache budget. `view` and `index` use the same
205
- task, CPU, and four-worker caps without the cache-budget term. An explicit value
204
+ worker per `64 MiB` of retained-cache budget. `view` and `index` use the same
205
+ task, CPU, and eight-worker caps without the cache-budget term. An explicit value
206
206
  from 1 through 32 overrides the automatic CPU and memory caps but is still
207
207
  limited by the number of tasks.
208
208
 
209
- Worker processes exit before retained-cache accounting. Their models and source
210
- text are streamed into the parent, so transient worker memory is separate from
211
- the retained navigation structures and the existing 80-percent warning. If an
212
- automatic pool fails before accepting a result, one automatic serial fallback
213
- is attempted; explicit worker counts fail instead of silently changing the
214
- requested configuration.
209
+ Worker processes build compact, detached navigation shards and exit before
210
+ retained-cache accounting. Full semantic object graphs and source text are not
211
+ retained in the parent. Tokenized source documents are loaded only for source
212
+ evidence requests such as `inspect`, and a byte-bounded LRU evicts older
213
+ documents. The transient worker memory remains separate from retained navigation
214
+ structures and the existing 80-percent warning. Retained bytes are maintained
215
+ incrementally; requests never traverse the complete cache object graph merely
216
+ to measure it. If an automatic pool fails before accepting a result, one
217
+ automatic serial fallback is attempted; explicit worker counts fail instead of
218
+ silently changing the requested configuration.
219
+
220
+ Cache prewarming builds the navigation registry directly without constructing
221
+ an empty-query result, symbol cards, pagination, or JSON payloads. Up to sixteen
222
+ recent ranked queries are retained in a small LRU so alternating CLI and
223
+ OpenCode searches remain warm.
215
224
 
216
225
  `cache start` waits up to `--startup-timeout 120` seconds by default for a large
217
226
  workspace to prewarm. The timeout belongs to the starting client and does not
@@ -0,0 +1 @@
1
+ __version__ = "2.2.0"
@@ -20,6 +20,8 @@ import threading
20
20
  import time
21
21
  from types import ModuleType
22
22
 
23
+ from watchfiles import watch
24
+
23
25
  from ._version import __version__
24
26
  from .agent_context import AgentContext
25
27
  from .agent_protocol import AgentProtocolError
@@ -32,10 +34,15 @@ DEFAULT_IDLE_TIMEOUT = 1800
32
34
  DEFAULT_STARTUP_TIMEOUT = 120.0
33
35
  _MAX_MESSAGE_BYTES = 1024 * 1024
34
36
  _CONNECTION_TIMEOUT = 2.0
37
+ _CLIENT_RESPONSE_TIMEOUT = 120.0
35
38
  _MEMORY_SIZE = re.compile(r"^(?P<count>[1-9][0-9]*)(?P<suffix>[KMG]?)$", re.IGNORECASE)
36
39
  _STARTUP_DIAGNOSTIC_BYTES = 16 * 1024
37
40
  _STARTUP_TOKEN_RE = re.compile(r"(?i)(token\b[^\n\r]*?:?\s*['\"]?[A-Za-z0-9_-]+['\"]?|\b[a-zA-Z0-9_-]{32,})")
38
41
  _START_LOCK_INCOMPLETE_GRACE_SECONDS = 1.0
42
+ _CACHE_REVISION_CHECK_INTERVAL_SECONDS = 3600.0
43
+ _WATCHED_SUFFIXES = frozenset(
44
+ {".pas", ".pp", ".inc", ".dpr", ".dpk", ".dproj", ".cfg"}
45
+ )
39
46
 
40
47
 
41
48
  def estimate_deep_size(value: object) -> int:
@@ -475,7 +482,7 @@ def _start_lock(root: str | Path, timeout: float):
475
482
  def _client_exchange(metadata: CacheMetadata, request: dict[str, object]) -> CacheClientResponse:
476
483
  try:
477
484
  with socket.create_connection(("127.0.0.1", metadata.port), timeout=2) as connection:
478
- connection.settimeout(3)
485
+ connection.settimeout(_CLIENT_RESPONSE_TIMEOUT)
479
486
  request_without_token = {key: value for key, value in request.items() if key != "token"}
480
487
  connection.sendall(json.dumps({"token": metadata.token, **request_without_token}, separators=(",", ":")).encode("utf-8") + b"\n")
481
488
  response = _read_line(connection)
@@ -541,6 +548,7 @@ class _CacheService:
541
548
  metadata.project_file or None,
542
549
  workers=metadata.workers,
543
550
  worker_memory_budget_bytes=metadata.max_memory_bytes,
551
+ revision_check_interval_seconds=_CACHE_REVISION_CHECK_INTERVAL_SECONDS,
544
552
  )
545
553
  self.budget = CacheBudget(metadata.max_memory_bytes)
546
554
  self.stats = CacheStats()
@@ -556,15 +564,14 @@ class _CacheService:
556
564
  def prewarm(self) -> None:
557
565
  started = time.monotonic()
558
566
  try:
559
- self.context.handle({"action": "find", "query": "", "max_items": 1, "max_chars": 256})
560
- self.last_revision = self.context.workspace.workspace_revision
567
+ self.last_revision = self.context.prewarm_navigation()
561
568
  self.cache_state = "warm"
562
569
  except AgentProtocolError as error:
563
570
  if error.code != "project_required":
564
571
  raise
565
572
  self.cache_state = "ready"
566
573
  self.last_budget = self.budget.enforce(
567
- measure=lambda: estimate_deep_size(self.context.cache_roots()),
574
+ measure=lambda: self.context.estimated_cache_bytes,
568
575
  evict_auxiliary=self.context.evict_auxiliary_caches,
569
576
  evict_navigation=self.context.evict_navigation_caches,
570
577
  )
@@ -605,7 +612,7 @@ class _CacheService:
605
612
  if before != after:
606
613
  self.stats.invalidations += 1
607
614
  self.last_budget = self.budget.enforce(
608
- measure=lambda: estimate_deep_size(self.context.cache_roots()),
615
+ measure=lambda: self.context.estimated_cache_bytes,
609
616
  evict_auxiliary=self.context.evict_auxiliary_caches,
610
617
  evict_navigation=self.context.evict_navigation_caches,
611
618
  )
@@ -642,10 +649,31 @@ class _CacheService:
642
649
  "parallel_seconds": self.context.parallel_stats.elapsed_seconds,
643
650
  "parallel_fallbacks": self.stats.parallel_fallbacks,
644
651
  "idle_timeout": self.metadata.idle_timeout, "idle_remaining": max(0.0, self.metadata.idle_timeout - idle),
645
- "workspace_revision": self.context.workspace.workspace_revision,
652
+ "workspace_revision": self.last_revision,
646
653
  }
647
654
 
648
655
 
656
+ def _watch_filter(_change: object, path: str) -> bool:
657
+ return Path(path).suffix.casefold() in _WATCHED_SUFFIXES
658
+
659
+
660
+ def _watch_workspace(service: _CacheService) -> None:
661
+ try:
662
+ for changes in watch(
663
+ service.metadata.root,
664
+ watch_filter=_watch_filter,
665
+ stop_event=service.shutdown,
666
+ debounce=50,
667
+ step=20,
668
+ recursive=True,
669
+ raise_interrupt=False,
670
+ ):
671
+ if changes:
672
+ service.context.invalidate_revision_cache()
673
+ except (OSError, RuntimeError):
674
+ service.context.invalidate_revision_cache()
675
+
676
+
649
677
  def _serve_connection(connection: socket.socket, service: _CacheService) -> None:
650
678
  try:
651
679
  line = _read_line(connection)
@@ -691,9 +719,17 @@ def run_cache_daemon(
691
719
  idle_timeout,
692
720
  time.time(),
693
721
  )
722
+ watcher: threading.Thread | None = None
694
723
  try:
695
724
  service = _CacheService(metadata)
696
725
  service.prewarm()
726
+ watcher = threading.Thread(
727
+ target=_watch_workspace,
728
+ args=(service,),
729
+ name="delphi-cache-watcher",
730
+ daemon=True,
731
+ )
732
+ watcher.start()
697
733
  _write_metadata(metadata)
698
734
  while not service.shutdown.is_set() and time.monotonic() - service.last_activity < idle_timeout:
699
735
  try:
@@ -704,6 +740,10 @@ def run_cache_daemon(
704
740
  connection.settimeout(_CONNECTION_TIMEOUT)
705
741
  _serve_connection(connection, service)
706
742
  finally:
743
+ if "service" in locals():
744
+ service.shutdown.set()
745
+ if watcher is not None:
746
+ watcher.join(timeout=2.0)
707
747
  listener.close()
708
748
  _remove_metadata_if_owned(metadata)
709
749