osa_tool 0.3.2__tar.gz → 0.3.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 (259) hide show
  1. {osa_tool-0.3.2 → osa_tool-0.3.4}/PKG-INFO +1 -1
  2. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/settings.py +4 -0
  3. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/git/git_agent.py +5 -2
  4. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/llm/llm.py +15 -5
  5. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/models/agent_status.py +1 -0
  6. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/docgen.py +27 -3
  7. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/sections/header.py +5 -1
  8. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/finalizer/agent.py +2 -1
  9. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/planner/agent.py +3 -1
  10. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/reviewer/agent.py +2 -2
  11. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/graph.py +1 -1
  12. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/state.py +2 -0
  13. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/run_chat.py +9 -1
  14. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/ui/input_for_chat.py +9 -1
  15. osa_tool-0.3.4/osa_tool/web_chat.py +255 -0
  16. {osa_tool-0.3.2 → osa_tool-0.3.4}/pyproject.toml +1 -1
  17. {osa_tool-0.3.2 → osa_tool-0.3.4}/LICENSE +0 -0
  18. {osa_tool-0.3.2 → osa_tool-0.3.4}/README.md +0 -0
  19. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/__init__.py +0 -0
  20. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/assets/notosans-sc-black.ttf +0 -0
  21. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/assets/notosans-sc-bold.ttf +0 -0
  22. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/assets/notosans-sc.ttf +0 -0
  23. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/__init__.py +0 -0
  24. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/locales/english.toml +0 -0
  25. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/locales/russian.toml +0 -0
  26. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/osa_config.py +0 -0
  27. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/about_section.toml +0 -0
  28. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/analysis.toml +0 -0
  29. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/docstring_generation.toml +0 -0
  30. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/osa_agent.toml +0 -0
  31. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/paper_analysis.toml +0 -0
  32. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/paper_claims.toml +0 -0
  33. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/readme/prompts.toml +0 -0
  34. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/readme/system_messages.toml +0 -0
  35. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/readme.toml +0 -0
  36. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/repo_organization.toml +0 -0
  37. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/repository_quality.toml +0 -0
  38. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/requirements.toml +0 -0
  39. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/scheduler.toml +0 -0
  40. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/prompts/system_messages.toml +0 -0
  41. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/settings/arguments.yaml +0 -0
  42. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/settings/config.toml +0 -0
  43. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/template.toml +0 -0
  44. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/autopep8.yml +0 -0
  45. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/black.yml +0 -0
  46. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/fix_pep8.yml +0 -0
  47. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/pep8.yml +0 -0
  48. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/pypi_publish.yml +0 -0
  49. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/ruff.yml +0 -0
  50. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/slash_command_dispatch.yml +0 -0
  51. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/github_gitverse/unit_test.yml +0 -0
  52. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/autopep8.yml +0 -0
  53. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/black.yml +0 -0
  54. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/fix_pep8.yml +0 -0
  55. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/pep8.yml +0 -0
  56. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/pypi_publish.yml +0 -0
  57. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/slash_command_dispatch.yml +0 -0
  58. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/config/templates/workflow/gitlab/unit_test.yml +0 -0
  59. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/__init__.py +0 -0
  60. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/git/__init__.py +0 -0
  61. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/git/metadata.py +0 -0
  62. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/git/request_utils.py +0 -0
  63. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/llm/__init__.py +0 -0
  64. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/llm/host.py +0 -0
  65. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/models/__init__.py +0 -0
  66. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/models/event.py +0 -0
  67. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/models/llm_output_models.py +0 -0
  68. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/core/models/task.py +0 -0
  69. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/images/osa_logo.PNG +0 -0
  70. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/images/osa_logo_transparent.PNG +0 -0
  71. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/templates/ci_config.toml +0 -0
  72. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/templates/community.toml +0 -0
  73. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/templates/contributing.toml +0 -0
  74. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/templates/licenses.toml +0 -0
  75. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/docs/templates/mkdocs.yml +0 -0
  76. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/__init__.py +0 -0
  77. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/__init__.py +0 -0
  78. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/artifacts.py +0 -0
  79. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/notebook_report/__init__.py +0 -0
  80. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/notebook_report/analyzer.py +0 -0
  81. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/notebook_report/models.py +0 -0
  82. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/notebook_report/report_maker.py +0 -0
  83. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_analysis/__init__.py +0 -0
  84. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_analysis/data_context.py +0 -0
  85. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_analysis/models.py +0 -0
  86. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_analysis/pipeline.py +0 -0
  87. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_analysis/verifier.py +0 -0
  88. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/__init__.py +0 -0
  89. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/claim_deduplicator.py +0 -0
  90. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/claim_extractor.py +0 -0
  91. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/claim_input_planner.py +0 -0
  92. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/claim_schemas.py +0 -0
  93. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/claim_validation.py +0 -0
  94. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/exceptions.py +0 -0
  95. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/io.py +0 -0
  96. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/marker_converter.py +0 -0
  97. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/models.py +0 -0
  98. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/openrouter_converter.py +0 -0
  99. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/pdf_splitter.py +0 -0
  100. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/pipeline.py +0 -0
  101. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/section_extractor.py +0 -0
  102. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/paper_claims/section_parser.py +0 -0
  103. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_quality/__init__.py +0 -0
  104. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_quality/checks.py +0 -0
  105. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_quality/repository_quality_scorer.py +0 -0
  106. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_quality/scoring_engine.py +0 -0
  107. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_report/__init__.py +0 -0
  108. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_report/report_generator.py +0 -0
  109. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_report/report_localization.py +0 -0
  110. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_report/report_maker.py +0 -0
  111. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/analysis/repository_report/response_validation.py +0 -0
  112. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/__init__.py +0 -0
  113. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/directory_translation/__init__.py +0 -0
  114. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/directory_translation/dirs_and_files_translator.py +0 -0
  115. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/__init__.py +0 -0
  116. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/adapters/__init__.py +0 -0
  117. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/adapters/base.py +0 -0
  118. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/adapters/javascript_adapter.py +0 -0
  119. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/adapters/python_adapter.py +0 -0
  120. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/adapters/typescript_adapter.py +0 -0
  121. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/core/__init__.py +0 -0
  122. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/core/osa_parser.py +0 -0
  123. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/core/source_view.py +0 -0
  124. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/docstring_generation.py +0 -0
  125. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/docstring_transformer.py +0 -0
  126. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/insert/__init__.py +0 -0
  127. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/insert/base_augmentor.py +0 -0
  128. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/insert/factory.py +0 -0
  129. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/insert/python_augmentor.py +0 -0
  130. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/insert/ts_js_augmentor.py +0 -0
  131. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/docstring_generation/topology.py +0 -0
  132. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/notebook_conversion/__init__.py +0 -0
  133. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/notebook_conversion/notebook_converter.py +0 -0
  134. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/__init__.py +0 -0
  135. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/__init__.py +0 -0
  136. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/__init__.py +0 -0
  137. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/base.py +0 -0
  138. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/cpp.py +0 -0
  139. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/csharp.py +0 -0
  140. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/factory.py +0 -0
  141. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/generic.py +0 -0
  142. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/go.py +0 -0
  143. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/java.py +0 -0
  144. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/javascript.py +0 -0
  145. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/kotlin.py +0 -0
  146. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/latex.py +0 -0
  147. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/python.py +0 -0
  148. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/ruby.py +0 -0
  149. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/rust.py +0 -0
  150. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/analyzers/swift.py +0 -0
  151. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/executor/action_executor.py +0 -0
  152. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/executor/batch_updater.py +0 -0
  153. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/health_checker.py +0 -0
  154. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/planning_manager.py +0 -0
  155. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/snapshot_manager.py +0 -0
  156. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/core/utils.py +0 -0
  157. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/organization/organize.py +0 -0
  158. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/requirements_generation/__init__.py +0 -0
  159. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/requirements_generation/requirements_generation.py +0 -0
  160. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/workflow_generation/README.md +0 -0
  161. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/workflow_generation/__init__.py +0 -0
  162. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/workflow_generation/workflow_executor.py +0 -0
  163. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/codebase/workflow_generation/workflow_generator.py +0 -0
  164. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/__init__.py +0 -0
  165. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/about_generation/__init__.py +0 -0
  166. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/about_generation/about_generator.py +0 -0
  167. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/__init__.py +0 -0
  168. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/codemeta_generator.py +0 -0
  169. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/community.py +0 -0
  170. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/contributing.py +0 -0
  171. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/docs_run.py +0 -0
  172. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/community_docs_generation/license_generation.py +0 -0
  173. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/__init__.py +0 -0
  174. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/inputs/__init__.py +0 -0
  175. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/inputs/article_content.py +0 -0
  176. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/inputs/article_path.py +0 -0
  177. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/inputs/pypi_status_checker.py +0 -0
  178. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/__init__.py +0 -0
  179. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/graph.py +0 -0
  180. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/llm_schemas.py +0 -0
  181. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/models.py +0 -0
  182. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/__init__.py +0 -0
  183. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/assembler.py +0 -0
  184. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/context_collector.py +0 -0
  185. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/deterministic_builder.py +0 -0
  186. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/intent_analyzer.py +0 -0
  187. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/readme_patch.py +0 -0
  188. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/section_generator.py +0 -0
  189. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/section_planner.py +0 -0
  190. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/self_eval.py +0 -0
  191. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/nodes/writer.py +0 -0
  192. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/runtime_context.py +0 -0
  193. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/section_catalog.py +0 -0
  194. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/section_catalog_entries.py +0 -0
  195. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/pipeline/state.py +0 -0
  196. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/readme_agent.py +0 -0
  197. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/readme_utils.py +0 -0
  198. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/sections/__init__.py +0 -0
  199. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/sections/icons/shieldsio_icons.json +0 -0
  200. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_generation/sections/installation.py +0 -0
  201. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_translation/__init__.py +0 -0
  202. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/docs/readme_translation/readme_translator.py +0 -0
  203. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/operations_catalog.py +0 -0
  204. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/operations/registry.py +0 -0
  205. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/__init__.py +0 -0
  206. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/__init__.py +0 -0
  207. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/executor/__init__.py +0 -0
  208. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/executor/agent.py +0 -0
  209. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/finalizer/__init__.py +0 -0
  210. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/finalizer/models.py +0 -0
  211. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/intent_router/__init__.py +0 -0
  212. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/intent_router/agent.py +0 -0
  213. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/intent_router/models.py +0 -0
  214. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/planner/__init__.py +0 -0
  215. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/planner/models.py +0 -0
  216. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/repo_analysis/__init__.py +0 -0
  217. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/repo_analysis/agent.py +0 -0
  218. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/reviewer/__init__.py +0 -0
  219. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/agents/reviewer/models.py +0 -0
  220. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/base.py +0 -0
  221. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/osa_agent/context.py +0 -0
  222. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/run.py +0 -0
  223. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/run_multi_process.py +0 -0
  224. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/scheduler/__init__.py +0 -0
  225. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/scheduler/plan.py +0 -0
  226. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/scheduler/response_validation.py +0 -0
  227. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/scheduler/scheduler.py +0 -0
  228. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/scheduler/workflow_manager.py +0 -0
  229. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/__init__.py +0 -0
  230. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/focused_cli.py +0 -0
  231. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_analysis/__init__.py +0 -0
  232. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_analysis/__main__.py +0 -0
  233. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_analysis/cli.py +0 -0
  234. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_claims/README.md +0 -0
  235. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_claims/__init__.py +0 -0
  236. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_claims/aggregate.py +0 -0
  237. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_claims/batch.py +0 -0
  238. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/paper_claims/evaluate.py +0 -0
  239. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/progress.py +0 -0
  240. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/__init__.py +0 -0
  241. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/dependencies.py +0 -0
  242. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/documentation.py +0 -0
  243. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/models.py +0 -0
  244. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/repo_analyzer.py +0 -0
  245. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/repo_graph.py +0 -0
  246. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/scorecard.py +0 -0
  247. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/sourcerank.py +0 -0
  248. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_analysis/testing.py +0 -0
  249. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_quality/__init__.py +0 -0
  250. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/tools/repository_quality/__main__.py +0 -0
  251. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/ui/__init__.py +0 -0
  252. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/ui/plan_editor.py +0 -0
  253. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/__init__.py +0 -0
  254. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/arguments_parser.py +0 -0
  255. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/logger.py +0 -0
  256. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/prompts_builder.py +0 -0
  257. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/response_cleaner.py +0 -0
  258. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/token_counter.py +0 -0
  259. {osa_tool-0.3.2 → osa_tool-0.3.4}/osa_tool/utils/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: osa_tool
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Tool that just makes your open source project better!
5
5
  License-Expression: BSD-3-Clause
6
6
  License-File: LICENSE
@@ -64,6 +64,10 @@ class GitSettings(BaseModel):
64
64
  else:
65
65
  raise ValueError(f"{self.repository} does not exist.")
66
66
  else:
67
+ # A URL copied from a browser often carries a trailing slash, and the
68
+ # remote helpers append ".git" to this value verbatim, which would make
69
+ # "owner/repo/" into "owner/repo/.git" and fail the clone.
70
+ self.repository = str(self.repository).rstrip("/")
67
71
  self.host_domain, self.host, self.name, self.full_name = parse_git_url(str(self.repository))
68
72
  return self
69
73
 
@@ -904,7 +904,7 @@ class GitHubAgent(GitAgent):
904
904
  else:
905
905
  logger.error(f"Failed to post a comment to PR #{pr_number}: {response.status_code} - {response.text}")
906
906
 
907
- def create_pull_request(self, title: str = None, body: str = None, changes: bool = False) -> None:
907
+ def create_pull_request(self, title: str = None, body: str = None, changes: bool = False) -> str | None:
908
908
  """Creates or updates a pull request on GitHub.
909
909
 
910
910
  This method implements intelligent PR management:
@@ -975,6 +975,7 @@ class GitHubAgent(GitAgent):
975
975
  f"updating PR #{pr_number}",
976
976
  raise_exception=False,
977
977
  )
978
+ return existing_pr.get("html_url")
978
979
  elif changes:
979
980
  report_files = self.get_attachment_branch_files()
980
981
  report_branch = "osa_tool_attachments"
@@ -997,7 +998,9 @@ class GitHubAgent(GitAgent):
997
998
 
998
999
  response = request_with_retry("post", url, json=pr_data, headers=headers)
999
1000
  if response.status_code == 201:
1000
- logger.info(f"GitHub pull request created successfully: {response.json()['html_url']}")
1001
+ pr_url = response.json()["html_url"]
1002
+ logger.info(f"GitHub pull request created successfully: {pr_url}")
1003
+ return pr_url
1001
1004
  else:
1002
1005
  self._handle_api_error(response, "creating pull request", raise_exception=True)
1003
1006
 
@@ -641,7 +641,7 @@ class ProtollmHandler(ModelHandler):
641
641
  time.sleep(retry_delay)
642
642
 
643
643
  logger.debug(repr(last_error))
644
- raise
644
+ raise last_error
645
645
 
646
646
  async def async_run_chain(
647
647
  self, prompt: str, parser: PydanticOutputParser, system_message: str = None, retry_delay: float = 0.5
@@ -702,15 +702,23 @@ class ProtollmHandler(ModelHandler):
702
702
  }
703
703
  return url_templates.get(self.model_settings.api, f"{self.model_settings.base_url};{model_name}")
704
704
 
705
- def _get_llm_params(self):
705
+ def _get_llm_params(self, model_name: str | None = None):
706
706
  """Extract LLM parameters from config"""
707
707
  llm_params = ["temperature", "max_tokens", "top_p"]
708
708
 
709
- return {
709
+ params = {
710
710
  name: getattr(self.model_settings, name)
711
711
  for name in llm_params
712
712
  if getattr(self.model_settings, name, None) is not None
713
713
  }
714
+ if (
715
+ self.model_settings.api == "openai"
716
+ and urlparse(self.model_settings.base_url).hostname == "api.openai.com"
717
+ and (model_name or self.model_settings.model).startswith(("gpt-6-", "gpt-6."))
718
+ ):
719
+ params.pop("temperature", None)
720
+ params.pop("top_p", None)
721
+ return params
714
722
 
715
723
  def _configure_api(self, model_name: str) -> None:
716
724
  """
@@ -727,10 +735,12 @@ class ProtollmHandler(ModelHandler):
727
735
  # 401 "User not found" errors)
728
736
  dotenv.load_dotenv(override=True)
729
737
 
738
+ params = self._get_llm_params(model_name)
739
+ if urlparse(self.model_settings.base_url).hostname != "api.openai.com":
740
+ params["extra_body"] = {"providers": {"only": self.model_settings.allowed_providers}}
730
741
  self.client = create_llm_connector(
731
742
  model_url=self._build_model_url(model_name),
732
- extra_body={"providers": {"only": self.model_settings.allowed_providers}},
733
- **self._get_llm_params(),
743
+ **params,
734
744
  )
735
745
 
736
746
  def _limit_tokens(
@@ -8,3 +8,4 @@ class AgentStatus(str, Enum):
8
8
  WAITING_FOR_USER = "waiting_for_user"
9
9
  ERROR = "error"
10
10
  COMPLETED = "completed"
11
+ STOPPED = "stopped"
@@ -1522,6 +1522,30 @@ class DocGen(object):
1522
1522
  dot_path = ".".join(processed_parts)
1523
1523
  return f"::: {dot_path}"
1524
1524
 
1525
+ @staticmethod
1526
+ def _path_inside_repo(target: str | Path, repo_path: Path) -> Path:
1527
+ """
1528
+ Returns a path relative to the repository root.
1529
+
1530
+ Analysis results carry absolute paths whenever OSA runs with --output.
1531
+ Dropping only the leading component then leaves the rest of the filesystem
1532
+ prefix in place, which nests osa_docs under the machine's own directory
1533
+ tree and, on Windows, pushes the result past the path length limit.
1534
+
1535
+ Args:
1536
+ target: The analysed file or directory path.
1537
+ repo_path: The resolved root of the analysed repository.
1538
+
1539
+ Returns:
1540
+ Path: The target expressed relative to the repository root.
1541
+ """
1542
+ candidate = Path(target)
1543
+ try:
1544
+ return candidate.resolve().relative_to(repo_path)
1545
+ except ValueError:
1546
+ # callers may pass a path that merely starts with the repository directory
1547
+ return Path(*candidate.parts[1:])
1548
+
1525
1549
  def generate_documentation_mkdocs(self, path: str, files_info, modules_info) -> None:
1526
1550
  """
1527
1551
  Generates MkDocs documentation for a Python project based on provided path.
@@ -1547,8 +1571,8 @@ class DocGen(object):
1547
1571
  continue
1548
1572
  parent_dir = Path(file).parent
1549
1573
  file_name = Path(file).name
1550
- relative_path = Path(*Path(file).parts[1::])
1551
- new_path = Path(init_doc_path, Path(*Path(parent_dir).parts[1::]))
1574
+ relative_path = self._path_inside_repo(file, repo_path)
1575
+ new_path = Path(init_doc_path, self._path_inside_repo(parent_dir, repo_path))
1552
1576
  new_path.mkdir(parents=True, exist_ok=True)
1553
1577
  text = (
1554
1578
  f"# {file_name.strip('.py').replace('_', ' ').title()}\n\n"
@@ -1559,7 +1583,7 @@ class DocGen(object):
1559
1583
  new_file.write_text(text)
1560
1584
 
1561
1585
  for module in modules_info:
1562
- new_file = Path(init_doc_path, Path(*Path(module).parts[1::]))
1586
+ new_file = Path(init_doc_path, self._path_inside_repo(module, repo_path))
1563
1587
  new_file.mkdir(parents=True, exist_ok=True)
1564
1588
  text = modules_info[module]
1565
1589
  Path(new_file, "index.md").write_text(text)
@@ -15,6 +15,10 @@ from osa_tool.tools.repository_analysis.sourcerank import SourceRank
15
15
  from osa_tool.utils.logger import logger
16
16
  from osa_tool.utils.utils import osa_project_root, resolve_repo_path, resolve_repo_web_identity
17
17
 
18
+ # Shields.io badge style applied to the tech icons. Every URL in shieldsio_icons.json
19
+ # carries a single "{0}" placeholder for it; "flat" matches the license badge above them.
20
+ BADGE_STYLE = "flat"
21
+
18
22
 
19
23
  class HeaderBuilder:
20
24
  """Produce the top-of-README header with project name, info badges, and tech badges."""
@@ -129,7 +133,7 @@ class HeaderBuilder:
129
133
  badges: list[str] = ["Built with:\n"]
130
134
  for tech in sorted(self.techs):
131
135
  if tech in icons:
132
- badges.append(f"![{tech}]({icons[tech][0]})")
136
+ badges.append(f"![{tech}]({icons[tech][0].format(BADGE_STYLE)})")
133
137
  if len(badges) >= self.max_tech_badges + 1:
134
138
  break
135
139
  if len(badges) <= 3:
@@ -45,6 +45,7 @@ class FinalizerAgent(BaseAgent):
45
45
 
46
46
  events = self._collect_events(state)
47
47
  summary = self._summarize_events(state, events) if events else ""
48
+ state.final_summary = summary
48
49
 
49
50
  # PR or logs
50
51
  if create_fork and create_pr:
@@ -56,7 +57,7 @@ class FinalizerAgent(BaseAgent):
56
57
  if about_message:
57
58
  pr_body += "\n\n---\n\n" + about_message
58
59
 
59
- self.context.git_agent.create_pull_request(
60
+ state.pull_request_url = self.context.git_agent.create_pull_request(
60
61
  body=pr_body,
61
62
  changes=changes,
62
63
  )
@@ -54,7 +54,9 @@ class PlannerAgent(BaseAgent):
54
54
 
55
55
  state.status = AgentStatus.ANALYZING
56
56
  logger.info("Planner started")
57
- register_all_operations()
57
+ # Installed wheels do not contain the source checkout's docs tree.
58
+ # Runtime registration must not attempt to regenerate developer docs.
59
+ register_all_operations(generate_docs=False)
58
60
 
59
61
  # Maintain plan history when re-planning after review: append last plan or clear for new request
60
62
  if state.active_request_source == "reviewer" and state.plan:
@@ -90,9 +90,9 @@ class ReviewerAgent(BaseAgent):
90
90
  if state.review_cycle_count >= state.max_review_cycles:
91
91
  self._reset_clarification(state)
92
92
  state.review_cycles_exhausted = True
93
- state.approval = True
93
+ state.status = AgentStatus.STOPPED
94
94
  logger.warning(
95
- "Max review cycles reached (%s/%s); routing to finalizer",
95
+ "Max review cycles reached (%s/%s); ending without finalization",
96
96
  state.review_cycle_count,
97
97
  state.max_review_cycles,
98
98
  )
@@ -70,7 +70,7 @@ def build_graph(context: AgentContext) -> Any:
70
70
  # reviewer → finalizer | planner
71
71
  graph.add_conditional_edges(
72
72
  "reviewer",
73
- lambda state: ("finalizer" if state.approval else "planner"),
73
+ lambda state: (END if state.review_cycles_exhausted else "finalizer" if state.approval else "planner"),
74
74
  )
75
75
 
76
76
  graph.add_edge("finalizer", END)
@@ -67,6 +67,8 @@ class OSAState(BaseModel):
67
67
  max_review_cycles: int = 3
68
68
  review_cycle_count: int = 0
69
69
  review_cycles_exhausted: bool = False
70
+ final_summary: Optional[str] = None
71
+ pull_request_url: Optional[str] = None
70
72
 
71
73
  model_config = ConfigDict(arbitrary_types_allowed=True)
72
74
 
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import sys
2
3
  import time
3
4
 
4
5
  from osa_tool.config.osa_config import OSAConfig
@@ -22,8 +23,15 @@ from osa_tool.utils.utils import (
22
23
  def main():
23
24
  # Create a command line argument parser
24
25
  parser = build_parser_from_yaml(extra_sections=["settings"])
26
+ parser.add_argument("--web-chat", action="store_true", help="Use the JSON-lines web chat protocol.")
27
+ parser.add_argument("--log-dir", help="Directory for OSA log files.")
25
28
  args = parser.parse_args()
26
29
 
30
+ if args.web_chat:
31
+ from osa_tool.web_chat import run_web_chat
32
+
33
+ return run_web_chat(args)
34
+
27
35
  # Collecting user parameters
28
36
  user_input: InitialChatInput = collect_user_input()
29
37
 
@@ -76,4 +84,4 @@ def main():
76
84
 
77
85
 
78
86
  if __name__ == "__main__":
79
- main()
87
+ sys.exit(main())
@@ -1,4 +1,5 @@
1
- from typing import Optional
1
+ from contextvars import ContextVar
2
+ from typing import Callable, Optional
2
3
 
3
4
  from pydantic import BaseModel
4
5
  from rich.console import Console
@@ -6,6 +7,9 @@ from rich.console import Console
6
7
  from osa_tool.osa_agent.state import OSAState
7
8
 
8
9
  console = Console()
10
+ _web_question_handler: ContextVar[Callable[["OSAState"], dict] | None] = ContextVar(
11
+ "web_question_handler", default=None
12
+ )
9
13
 
10
14
 
11
15
  class InitialChatInput(BaseModel):
@@ -57,6 +61,10 @@ def wait_for_user_clarification(state: OSAState) -> dict:
57
61
  if not state.clarification_required:
58
62
  raise RuntimeError("wait_for_user_clarification called but no clarification was requested")
59
63
 
64
+ web_handler = _web_question_handler.get()
65
+ if web_handler is not None:
66
+ return web_handler(state)
67
+
60
68
  payload = state.clarification_payload or {}
61
69
  question = payload.get("question", "Additional information required:")
62
70
  fields = payload.get("fields", [])
@@ -0,0 +1,255 @@
1
+ """JSON-lines transport for the agent graph used by browser clients.
2
+
3
+ The terminal chat remains in :mod:`osa_tool.run_chat`. Only this opt-in mode
4
+ uses stdin and stdout as a machine-readable protocol.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import os
11
+ import sys
12
+ import uuid
13
+ from contextlib import contextmanager
14
+ from pathlib import Path
15
+ from typing import Any, TextIO
16
+
17
+ from osa_tool.config.osa_config import OSAConfig
18
+ from osa_tool.config.settings import ConfigManager
19
+ from osa_tool.core.models.agent_status import AgentStatus
20
+ from osa_tool.osa_agent.context import AgentContext
21
+ from osa_tool.osa_agent.graph import build_graph
22
+ from osa_tool.osa_agent.state import OSAState
23
+ from osa_tool.run import initialize_git_platform, resolve_publishing_options
24
+ from osa_tool.ui.input_for_chat import _web_question_handler
25
+ from osa_tool.utils.logger import logger, setup_logging
26
+ from osa_tool.utils.utils import (
27
+ osa_project_root,
28
+ parse_folder_name,
29
+ prepare_local_output_repository,
30
+ switch_to_output_directory,
31
+ )
32
+
33
+
34
+ class ChatProtocolError(ValueError):
35
+ """The browser sent an invalid protocol message."""
36
+
37
+
38
+ class ChatCancelled(Exception):
39
+ """The browser closed or cancelled an interactive run."""
40
+
41
+
42
+ class JsonLineChatIO:
43
+ def __init__(self, source: TextIO, sink: TextIO):
44
+ self.source = source
45
+ self.sink = sink
46
+ self.output_dir: Path | None = None
47
+
48
+ def emit(self, event_type: str, **fields: Any) -> None:
49
+ self.sink.write(json.dumps({"type": event_type, **fields}, default=str) + "\n")
50
+ self.sink.flush()
51
+
52
+ def _read(self) -> dict[str, Any]:
53
+ line = self.source.readline(1_048_577)
54
+ if not line:
55
+ raise ChatCancelled("Browser connection closed")
56
+ if len(line) > 1_048_576:
57
+ raise ChatProtocolError("Message is too large")
58
+ try:
59
+ value = json.loads(line)
60
+ except json.JSONDecodeError as exc:
61
+ raise ChatProtocolError("Expected a JSON object") from exc
62
+ if not isinstance(value, dict):
63
+ raise ChatProtocolError("Expected a JSON object")
64
+ if value.get("type") == "cancel":
65
+ raise ChatCancelled("Run cancelled")
66
+ return value
67
+
68
+ def read_start(self) -> dict[str, str | None]:
69
+ message = self._read()
70
+ repository = message.get("repository")
71
+ request = message.get("request")
72
+ attachment = message.get("attachment")
73
+ if message.get("type") != "start":
74
+ raise ChatProtocolError("First message must have type 'start'")
75
+ if not isinstance(repository, str) or not repository.strip():
76
+ raise ChatProtocolError("Repository is required")
77
+ if not isinstance(request, str) or not request.strip():
78
+ raise ChatProtocolError("Request is required")
79
+ if attachment is not None and not isinstance(attachment, str):
80
+ raise ChatProtocolError("Attachment must be a string or null")
81
+ return {
82
+ "repository": repository.strip(),
83
+ "request": request.strip(),
84
+ "attachment": attachment.strip() or None if isinstance(attachment, str) else None,
85
+ }
86
+
87
+ def ask(self, state: OSAState) -> dict[str, str | None]:
88
+ payload = state.clarification_payload or {}
89
+ fields = payload.get("fields", [])
90
+ question_id = uuid.uuid4().hex
91
+ if state.clarification_type == "review":
92
+ self.emit(
93
+ "review",
94
+ tasks=_task_results(state),
95
+ artifacts=_artifact_paths(state, self.output_dir),
96
+ events=_artifact_events(state),
97
+ )
98
+ self.emit(
99
+ "question",
100
+ id=question_id,
101
+ agent=state.clarification_agent,
102
+ kind=state.clarification_type,
103
+ question=payload.get("question", "Additional information required"),
104
+ fields=fields,
105
+ )
106
+ while True:
107
+ try:
108
+ message = self._read()
109
+ if message.get("type") != "answer" or message.get("id") != question_id:
110
+ raise ChatProtocolError("Answer does not match the pending question")
111
+ values = message.get("values")
112
+ if not isinstance(values, dict):
113
+ raise ChatProtocolError("Answer values must be an object")
114
+ names = {field["name"] for field in fields}
115
+ if set(values) - names:
116
+ raise ChatProtocolError("Answer contains unknown fields")
117
+ answers: dict[str, str | None] = {}
118
+ for field in fields:
119
+ name = field["name"]
120
+ value = values.get(name)
121
+ if value is not None and not isinstance(value, str):
122
+ raise ChatProtocolError(f"{name} must be text")
123
+ value = value.strip() if value else None
124
+ if field.get("required") and not value:
125
+ raise ChatProtocolError(f"{name} is required")
126
+ if name == "accept" and value and value.lower() not in {"yes", "y", "no", "n"}:
127
+ raise ChatProtocolError("Approval must be yes or no")
128
+ answers[name] = value
129
+ return answers
130
+ except ChatProtocolError as exc:
131
+ self.emit("error", message=str(exc), recoverable=True)
132
+
133
+
134
+ @contextmanager
135
+ def use_web_questions(handler):
136
+ token = _web_question_handler.set(handler)
137
+ try:
138
+ yield
139
+ finally:
140
+ _web_question_handler.reset(token)
141
+
142
+
143
+ def _artifact_paths(state: OSAState, output_dir: Path | None) -> list[str]:
144
+ if output_dir is None:
145
+ return []
146
+ paths: set[str] = set()
147
+ for artifact in state.artifacts.values():
148
+ if not isinstance(artifact, dict):
149
+ continue
150
+ for event in artifact.get("events", []):
151
+ target = event.get("target") if isinstance(event, dict) else getattr(event, "target", None)
152
+ if not isinstance(target, (str, Path)):
153
+ continue
154
+ path = Path(target)
155
+ if not path.is_absolute():
156
+ path = Path(state.repo_path or output_dir) / path
157
+ path = path.resolve()
158
+ if path.is_file() and path.is_relative_to(output_dir):
159
+ paths.add(str(path))
160
+ return sorted(paths)
161
+
162
+
163
+ def _task_results(state: OSAState) -> list[dict[str, Any]]:
164
+ return [{"id": task.id, "status": task.status.value, "result": task.result} for task in state.plan]
165
+
166
+
167
+ def _artifact_events(state: OSAState) -> list[dict[str, str]]:
168
+ events: list[dict[str, str]] = []
169
+ for artifact in state.artifacts.values():
170
+ if not isinstance(artifact, dict):
171
+ continue
172
+ for event in artifact.get("events", []):
173
+ kind = event.get("kind") if isinstance(event, dict) else getattr(event, "kind", None)
174
+ target = event.get("target") if isinstance(event, dict) else getattr(event, "target", None)
175
+ if kind is not None and target is not None:
176
+ events.append({"kind": str(getattr(kind, "value", kind)), "target": str(target)})
177
+ return events[:50]
178
+
179
+
180
+ def _run(args, io: JsonLineChatIO) -> int:
181
+ start = io.read_start()
182
+ args.repository = start["repository"]
183
+ output_dir = Path(args.output).resolve() if args.output else None
184
+ io.output_dir = output_dir
185
+ logs_dir = args.log_dir or str(Path(osa_project_root()).parent / "logs")
186
+ setup_logging(parse_folder_name(args.repository), logs_dir)
187
+
188
+ if output_dir is not None:
189
+ switch_to_output_directory(output_dir)
190
+ if os.path.isdir(args.repository):
191
+ args.repository = str(prepare_local_output_repository(args.repository, output_dir))
192
+
193
+ config_manager = ConfigManager(args)
194
+ git_agent, workflow_manager = initialize_git_platform(args, config_manager)
195
+ create_fork, create_pull_request = resolve_publishing_options(args, git_agent)
196
+ context = AgentContext(
197
+ OSAConfig(
198
+ config_manager=config_manager,
199
+ git_agent=git_agent,
200
+ workflow_manager=workflow_manager,
201
+ create_fork=create_fork,
202
+ create_pull_request=create_pull_request,
203
+ delete_dir=args.delete_dir,
204
+ enable_replanning=True,
205
+ enable_memory=True,
206
+ )
207
+ )
208
+ state = OSAState(
209
+ repo_url=start["repository"],
210
+ attachment=start["attachment"],
211
+ active_request=start["request"],
212
+ active_request_source="user",
213
+ session_id=uuid.uuid4().hex,
214
+ status=AgentStatus.INIT,
215
+ )
216
+ io.emit("started", session_id=state.session_id)
217
+ with use_web_questions(io.ask):
218
+ for value in build_graph(context).stream(state, stream_mode="values"):
219
+ state = OSAState.model_validate(value)
220
+ if state.active_agent:
221
+ io.emit("progress", agent=state.active_agent, status=state.status.value)
222
+
223
+ if state.review_cycles_exhausted:
224
+ io.emit("stopped", reason="review_cycles_exhausted", message="Revision limit reached without approval")
225
+ return 0
226
+ if state.status != AgentStatus.COMPLETED:
227
+ raise RuntimeError(f"Agent graph ended with status {state.status.value}")
228
+ io.emit(
229
+ "completed",
230
+ summary=state.final_summary or "Run completed",
231
+ tasks=_task_results(state),
232
+ artifacts=_artifact_paths(state, output_dir),
233
+ pull_request_url=state.pull_request_url,
234
+ )
235
+ return 0
236
+
237
+
238
+ def run_web_chat(args) -> int:
239
+ """Run the web protocol with stdout reserved exclusively for JSON lines."""
240
+ sys.stdout.flush()
241
+ wire = os.fdopen(os.dup(sys.stdout.fileno()), "w", encoding="utf-8", buffering=1)
242
+ os.dup2(sys.stderr.fileno(), sys.stdout.fileno())
243
+ sys.stdout = sys.stderr
244
+ io = JsonLineChatIO(sys.stdin, wire)
245
+ try:
246
+ return _run(args, io)
247
+ except ChatCancelled as exc:
248
+ io.emit("stopped", reason="cancelled", message=str(exc))
249
+ return 0
250
+ except Exception as exc:
251
+ logger.exception("Web chat failed")
252
+ io.emit("error", message=str(exc), recoverable=False)
253
+ return 1
254
+ finally:
255
+ wire.close()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "osa_tool"
3
- version = "0.3.2"
3
+ version = "0.3.4"
4
4
  description = "Tool that just makes your open source project better!"
5
5
  requires-python = ">=3.11,<4.0"
6
6
  authors = [
File without changes
File without changes
File without changes