agentic-dev-loop 0.15.2__tar.gz → 0.15.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. {agentic_dev_loop-0.15.2/src/agentic_dev_loop.egg-info → agentic_dev_loop-0.15.3}/PKG-INFO +3 -1
  2. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/pyproject.toml +12 -1
  3. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/brief_coverage.py +33 -10
  4. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/brief_prompt.md +10 -7
  5. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/brief_prompt.py +4 -4
  6. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/stages.py +2 -7
  7. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3/src/agentic_dev_loop.egg-info}/PKG-INFO +3 -1
  8. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop.egg-info/requires.txt +2 -0
  9. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_041_brief_coverage.py +45 -4
  10. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_046_brief_prompt.py +8 -0
  11. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_outputs_pbt.py +6 -2
  12. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/LICENSE +0 -0
  13. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/README.md +0 -0
  14. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/setup.cfg +0 -0
  15. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/__init__.py +0 -0
  16. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/core/__init__.py +0 -0
  17. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/core/config.py +0 -0
  18. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/core/failure.py +0 -0
  19. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/core/paths.py +0 -0
  20. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/core/profiles/__init__.py +0 -0
  21. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/core/profiles/data/dotnet-node.yaml +0 -0
  22. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/core/profiles/data/python.yaml +0 -0
  23. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/core/results.py +0 -0
  24. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/core/verdict.py +0 -0
  25. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/__init__.py +0 -0
  26. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/actions.py +0 -0
  27. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/agent_error_guide.md +0 -0
  28. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/agent_error_guide.py +0 -0
  29. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/agents.py +0 -0
  30. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/allowlist.py +0 -0
  31. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/cli.py +0 -0
  32. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/completion.py +0 -0
  33. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/containers.py +0 -0
  34. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/copilot_auth.py +0 -0
  35. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/deps.py +0 -0
  36. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/dryrun.py +0 -0
  37. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
  38. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/egress_proxy.py +0 -0
  39. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
  40. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
  41. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/forbidden.py +0 -0
  42. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/graph.py +0 -0
  43. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/hostboundary.py +0 -0
  44. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/isolation.py +0 -0
  45. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/llm.py +0 -0
  46. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/loaders.py +0 -0
  47. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/mounts.py +0 -0
  48. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/__init__.py +0 -0
  49. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/checks.py +0 -0
  50. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/doctor.py +0 -0
  51. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/init.py +0 -0
  52. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/recipes/dotnet-node.yaml +0 -0
  53. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/recipes/python.yaml +0 -0
  54. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/recipes.py +0 -0
  55. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/templates/dev.agent.md +0 -0
  56. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/templates/env.example +0 -0
  57. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/templates/qa.agent.md +0 -0
  58. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/templates/review.agent.md +0 -0
  59. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/templates.py +0 -0
  60. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/onboarding/upgrade.py +0 -0
  61. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/outputs.py +0 -0
  62. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/presets.py +0 -0
  63. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/proc.py +0 -0
  64. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/provider_wire.py +0 -0
  65. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/registry.py +0 -0
  66. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/run_status.py +0 -0
  67. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/run_status_command.md +0 -0
  68. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
  69. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
  70. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/secrets.py +0 -0
  71. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/step_workspace.py +0 -0
  72. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/engine/sync_back.py +0 -0
  73. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/py.typed +0 -0
  74. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/reporting/__init__.py +0 -0
  75. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/reporting/metrics.py +0 -0
  76. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/reporting/render.py +0 -0
  77. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/reporting/sources.py +0 -0
  78. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/viz/__init__.py +0 -0
  79. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/viz/layout.py +0 -0
  80. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/viz/page.py +0 -0
  81. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/viz/runs_view.py +0 -0
  82. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop/viz/server.py +0 -0
  83. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop.egg-info/SOURCES.txt +0 -0
  84. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
  85. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
  86. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
  87. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_029_merge_on_conflict.py +0 -0
  88. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_033_brief_step.py +0 -0
  89. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_035_finalize_nudge.py +0 -0
  90. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_036_banned_commands_and_lane_boundary.py +0 -0
  91. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_055_cli.py +0 -0
  92. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_055_doctor.py +0 -0
  93. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_055_init.py +0 -0
  94. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_055_profile_loading.py +0 -0
  95. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_055_profiles.py +0 -0
  96. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_055_recipes.py +0 -0
  97. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_055_schema_lock.py +0 -0
  98. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_055_templates.py +0 -0
  99. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_055_upgrade.py +0 -0
  100. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_actions.py +0 -0
  101. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_agent_error_guide.py +0 -0
  102. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_agents.py +0 -0
  103. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_aidlc.py +0 -0
  104. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_build_viz_page.py +0 -0
  105. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_cli.py +0 -0
  106. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_config.py +0 -0
  107. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_containers.py +0 -0
  108. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_copilot_auth.py +0 -0
  109. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_copilot_cli.py +0 -0
  110. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_deps.py +0 -0
  111. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_dry_run.py +0 -0
  112. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_e2e_fixtures.py +0 -0
  113. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_egress_proxy.py +0 -0
  114. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_graph.py +0 -0
  115. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_hostboundary.py +0 -0
  116. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_hostboundary_integration.py +0 -0
  117. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_isolation.py +0 -0
  118. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_isolation_integration.py +0 -0
  119. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_isolation_pbt.py +0 -0
  120. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_llm.py +0 -0
  121. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_loaders.py +0 -0
  122. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_module_contract.py +0 -0
  123. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_outputs.py +0 -0
  124. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_proc.py +0 -0
  125. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_provider_wire.py +0 -0
  126. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_publish.py +0 -0
  127. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_qa_prompt.py +0 -0
  128. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_registry.py +0 -0
  129. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_repo_ascii.py +0 -0
  130. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_results.py +0 -0
  131. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_run_status.py +0 -0
  132. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_runs_js_filters.py +0 -0
  133. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_runs_js_key_stats.py +0 -0
  134. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_runs_js_metrics.py +0 -0
  135. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_runs_layout.py +0 -0
  136. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_runs_layout_key_stats.py +0 -0
  137. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_sdk_compat.py +0 -0
  138. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_secrets.py +0 -0
  139. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_secrets_integration.py +0 -0
  140. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_secrets_pipeline.py +0 -0
  141. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_source_resolution.py +0 -0
  142. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_stages.py +0 -0
  143. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_step_workspace.py +0 -0
  144. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_sync_back.py +0 -0
  145. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_unit11.py +0 -0
  146. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_unit11_pbt.py +0 -0
  147. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_unit12_reporting.py +0 -0
  148. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_unit13_runs_viz.py +0 -0
  149. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_unit5_config.py +0 -0
  150. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_unit5_pbt.py +0 -0
  151. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_unit5_pure.py +0 -0
  152. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_verdict.py +0 -0
  153. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_viz.py +0 -0
  154. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_viz_demo.py +0 -0
  155. {agentic_dev_loop-0.15.2 → agentic_dev_loop-0.15.3}/tests/test_viz_layout.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 0.15.2
3
+ Version: 0.15.3
4
4
  Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
5
5
  Author: Luke Wilson
6
6
  License-Expression: Apache-2.0
@@ -29,6 +29,8 @@ Requires-Dist: pydantic>=2.0
29
29
  Requires-Dist: PyYAML>=6.0
30
30
  Requires-Dist: openhands-sdk~=1.17.0
31
31
  Requires-Dist: openhands-tools~=1.17.0
32
+ Requires-Dist: browser-use==0.11.13
33
+ Requires-Dist: pillow>=13.0.0
32
34
  Requires-Dist: agent-client-protocol<1.0.0
33
35
  Requires-Dist: langgraph>=1.2
34
36
  Requires-Dist: langgraph-checkpoint-sqlite>=3.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentic-dev-loop"
7
- version = "0.15.2"
7
+ version = "0.15.3"
8
8
  description = "Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -36,6 +36,17 @@ dependencies = [
36
36
  "PyYAML>=6.0",
37
37
  "openhands-sdk ~=1.17.0",
38
38
  "openhands-tools ~=1.17.0",
39
+ # browser-use is a transitive dep (openhands-tools declares
40
+ # browser-use>=0.8.0, unpinned). 0.12.0+ hard-pin pillow==12.x, and
41
+ # pillow 12.3.0 carries 39 CVEs (fixed in 13.0.0); pin the last release
42
+ # with an open pillow constraint (>=11.2.1) so a fresh pip resolve lands
43
+ # on pillow 13.0.0 and the restore step's pip-audit stays green.
44
+ "browser-use==0.11.13",
45
+ # pillow is a transitive dep (browser-use, reportlab). 12.3.0 carries 39
46
+ # CVEs (fixed in 13.0.0); pin a floor so the restore step's pip-audit
47
+ # stays green, and so a future browser-use pin back to ==12.x fails the
48
+ # install loudly instead of silently reintroducing the CVEs.
49
+ "pillow>=13.0.0",
39
50
  # openhands-sdk declares agent-client-protocol>=0.8.1 with NO upper bound;
40
51
  # the 1.0.0rc pre-releases drop acp.helpers (breaks sdk 1.17.0 imports).
41
52
  # Without this cap, --pre installs resolve to the broken rc. See AGENTS.md
@@ -11,11 +11,26 @@ and file-agnostic.
11
11
  from __future__ import annotations
12
12
 
13
13
  import re
14
+ import unicodedata
15
+
16
+
17
+ _GLYPH_FOLD = str.maketrans({
18
+ "\u2010": "-", "\u2011": "-", "\u2012": "-",
19
+ "\u2013": "-", "\u2014": "-", "\u2015": "-",
20
+ "\u2018": "'", "\u2019": "'",
21
+ "\u201c": '"', "\u201d": '"',
22
+ })
23
+
24
+
25
+ def _fold_glyphs(text: str) -> str:
26
+ """Fold compatibility glyphs and collapse whitespace, preserving case."""
27
+ folded = unicodedata.normalize("NFKC", text).translate(_GLYPH_FOLD)
28
+ return " ".join(folded.split())
14
29
 
15
30
 
16
31
  def _normalize(text: str) -> str:
17
- """Case-fold and collapse whitespace - the matching normal form."""
18
- return " ".join(text.split()).casefold()
32
+ """Case-folded, glyph-folded, whitespace-collapsed matching form."""
33
+ return _fold_glyphs(text).casefold()
19
34
 
20
35
 
21
36
  def _normalize_line(line: str) -> str:
@@ -23,7 +38,7 @@ def _normalize_line(line: str) -> str:
23
38
 
24
39
  A compliant digest writes anchors as headings on their own line, wrapped
25
40
  in ``**`` and (for the four section headings) prefixed with ``##``. List
26
- bullets are tolerated; the whole-line comparison is what makes a mid-line
41
+ bullets are tolerated; the line-prefix comparison is what makes a mid-line
27
42
  prose mention fail."""
28
43
  stripped = line.strip().replace("**", "")
29
44
  stripped = re.sub(r"^#+\s*", "", stripped)
@@ -67,19 +82,27 @@ def missing_anchors(brief_text: str, anchors: list[str],
67
82
  """Return every required item the captured brief fails to cover.
68
83
 
69
84
  Required section headings (from the configured ``brief_inputs.sections``)
70
- and anchors are matched line-anchored (case-insensitive,
71
- whitespace-collapsed, ``**`` decoration stripped). Env facts are matched
72
- verbatim, exactly as handed to the brief agent. An empty list means the
85
+ and anchors are matched as line prefixes (case-insensitive,
86
+ glyph-folded, whitespace-collapsed, decoration stripped): an item is
87
+ covered when it starts a line, so content may follow on that line. A
88
+ mid-line mention does not count. Env facts are matched as glyph-folded,
89
+ whitespace-collapsed, case-sensitive substrings. An empty list means the
73
90
  digest passes coverage."""
74
91
  missing: list[str] = []
75
- lines = {_normalize_line(line) for line in brief_text.splitlines()}
92
+ lines = [_normalize_line(line) for line in brief_text.splitlines()]
93
+ lines = [line for line in lines if line]
94
+
95
+ def covered(item: str) -> bool:
96
+ return any(line.startswith(item) for line in lines)
97
+
76
98
  for section in sections:
77
- if _normalize(section) not in lines:
99
+ if not covered(_normalize(section)):
78
100
  missing.append(section)
79
101
  for anchor in anchors:
80
- if anchor not in lines:
102
+ if not covered(anchor):
81
103
  missing.append(anchor)
104
+ folded_brief = _fold_glyphs(brief_text)
82
105
  for fact in env_facts:
83
- if fact not in brief_text:
106
+ if _fold_glyphs(fact) not in folded_brief:
84
107
  missing.append(fact)
85
108
  return missing
@@ -26,21 +26,24 @@ a compact brief that the dev and review agents reuse on every attempt.
26
26
  attempt. Do not invent them and do not re-derive them.
27
27
  - The repo-rules digest is structural: one line per top-level section of each
28
28
  repo-rule input. Markdown anchors are the `##` headings: keep each heading
29
- verbatim, then compress that section's rules into the one line that follows
30
- it. YAML anchors are each `- name:` entry under `categories:` plus the
29
+ verbatim at the start of its own line, then compress that section's rules
30
+ into the text that follows on that line (after a colon) or on the next
31
+ line. YAML anchors are each `- name:` entry under `categories:` plus the
31
32
  `enforcement_levels` preamble: keep each name verbatim. No top-level section
32
33
  or category may be dropped; merging is allowed only between a section and
33
- its own subsections. The anchor line holds the heading and nothing else; the
34
- compressed rules go on the following line:
34
+ its own subsections:
35
35
 
36
36
  - general_principles
37
37
  Simplest thing that works; defer decisions to the last responsible moment.
38
38
 
39
- A line like `general_principles: Simplest thing that works; ...` does not
40
- satisfy coverage; the heading must be the only text on its line.
39
+ A line like `general_principles: Simplest thing that works; ...` passes too:
40
+ the heading must start its line.
41
41
  - Keep the environment-facts entries verbatim: each fact and the behavior it
42
42
  implies travel together.
43
- - ASCII only. Do not use em dash characters.
43
+ - ASCII only, except that a required heading, env fact, or configured section
44
+ heading must be reproduced character-exact: when such an item contains a
45
+ non-ASCII character, keep that exact character, never transliterate it. Do
46
+ not introduce em dashes or other non-ASCII characters of your own.
44
47
  - If the task message carries a `## Coverage retry` section, regenerate the
45
48
  brief so every listed item appears, verbatim where the contract requires
46
49
  verbatim. No exploration: the listed items are the only correction signal.
@@ -15,7 +15,7 @@ from __future__ import annotations
15
15
  from importlib.resources import files
16
16
 
17
17
  BRIEF_PROMPT_RESOURCE = "brief_prompt.md"
18
- BRIEF_PROMPT_VERSION = "1"
18
+ BRIEF_PROMPT_VERSION = "2"
19
19
 
20
20
 
21
21
  def load_template() -> str:
@@ -36,9 +36,9 @@ def _output_block(sections: list[str]) -> str:
36
36
  "compressed content for that section (write `None` when a section has "
37
37
  "nothing):\n\n"
38
38
  f"{headings}\n\n"
39
- f"The section headings must appear exactly as written above: {names}. "
40
- "Do not shorten or rename them, and do not merge a section with "
41
- "another section."
39
+ "The section headings must appear at the start of their own lines, as "
40
+ f"written above: {names}. Do not shorten or rename them, and do not "
41
+ "merge a section with another section."
42
42
  )
43
43
 
44
44
 
@@ -678,16 +678,11 @@ def build_brief_context(runtime: Runtime, step: StepConfig,
678
678
  parts.append(
679
679
  "## Coverage retry\n\n"
680
680
  "The captured brief is missing the following required items. "
681
- "Regenerate the digest so every listed item occupies its own "
682
- "line, exactly as listed: the item alone on the line, no "
683
- "trailing colon and no content. Put the compressed content on "
684
- "the following line:\n\n"
681
+ "Regenerate the digest so every listed item starts its own line; "
682
+ "compressed content may follow on that line or the next line.\n\n"
685
683
  "- general_principles\n"
686
684
  " Simplest thing that works; defer decisions to the last "
687
685
  "responsible moment.\n\n"
688
- "A line like `general_principles: Simplest thing that works; "
689
- "...` does not satisfy coverage; the item must be the only "
690
- "text on its line:\n\n"
691
686
  + coverage_note)
692
687
  return "\n\n".join(parts)
693
688
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 0.15.2
3
+ Version: 0.15.3
4
4
  Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
5
5
  Author: Luke Wilson
6
6
  License-Expression: Apache-2.0
@@ -29,6 +29,8 @@ Requires-Dist: pydantic>=2.0
29
29
  Requires-Dist: PyYAML>=6.0
30
30
  Requires-Dist: openhands-sdk~=1.17.0
31
31
  Requires-Dist: openhands-tools~=1.17.0
32
+ Requires-Dist: browser-use==0.11.13
33
+ Requires-Dist: pillow>=13.0.0
32
34
  Requires-Dist: agent-client-protocol<1.0.0
33
35
  Requires-Dist: langgraph>=1.2
34
36
  Requires-Dist: langgraph-checkpoint-sqlite>=3.0
@@ -2,6 +2,8 @@ pydantic>=2.0
2
2
  PyYAML>=6.0
3
3
  openhands-sdk~=1.17.0
4
4
  openhands-tools~=1.17.0
5
+ browser-use==0.11.13
6
+ pillow>=13.0.0
5
7
  agent-client-protocol<1.0.0
6
8
  langgraph>=1.2
7
9
  langgraph-checkpoint-sqlite>=3.0
@@ -142,6 +142,46 @@ class TestMissingAnchors:
142
142
  ["offline; never fetch"], SECTIONS) == []
143
143
 
144
144
 
145
+ def test_inline_anchor_content_line_passes(self):
146
+ text = _brief_ok().replace(
147
+ "**Error handling**\nNever fall back silently.\n\n",
148
+ "**Error handling**: Never fall back silently.\n\n")
149
+ assert missing_anchors(text, ["error handling", "mock policy"],
150
+ ["offline; never fetch"], SECTIONS) == []
151
+
152
+ def test_em_dash_heading_transliterated_line_passes(self):
153
+ anchors = extract_anchors(
154
+ "## Default development mode \u2014 ask before coding\n")
155
+ assert anchors == ["default development mode - ask before coding"]
156
+ text = _brief_ok().replace(
157
+ "**Error handling**\nNever fall back silently.\n\n",
158
+ "**Default development mode - ask before coding**\n"
159
+ "Ask before writing code.\n\n")
160
+ assert missing_anchors(text, anchors, ["offline; never fetch"],
161
+ SECTIONS) == []
162
+
163
+ def test_curly_quote_heading_folded(self):
164
+ anchors = extract_anchors("## User\u2019s rules\n")
165
+ assert anchors == ["user's rules"]
166
+ text = _brief_ok().replace(
167
+ "**Error handling**\nNever fall back silently.\n\n",
168
+ "**User's rules**\nApply them.\n\n")
169
+ assert missing_anchors(text, anchors, ["offline; never fetch"],
170
+ SECTIONS) == []
171
+
172
+ def test_env_fact_em_dash_tolerated(self):
173
+ text = _brief_ok().replace("offline; never fetch",
174
+ "offline - never fetch")
175
+ assert missing_anchors(text, ["error handling", "mock policy"],
176
+ ["offline \u2014 never fetch"], SECTIONS) == []
177
+
178
+ def test_env_fact_case_still_matters(self):
179
+ text = _brief_ok().replace("offline; never fetch",
180
+ "the build is efficient")
181
+ assert "CI" in missing_anchors(
182
+ text, ["error handling", "mock policy"], ["CI"], SECTIONS)
183
+
184
+
145
185
  class TestCoverageNoteWording:
146
186
  """The retry note must state the line rule unambiguously (043): a
147
187
  correction that the model can misread as "the item starts its own line"
@@ -154,11 +194,12 @@ class TestCoverageNoteWording:
154
194
  runtime, runtime.cfg.steps[0], {"plan.md": "THE PLAN"},
155
195
  coverage_note=note)
156
196
  assert "## Coverage retry" in message
157
- assert ("the item alone on the line, no trailing colon and no "
158
- "content") in message
197
+ assert ("Regenerate the digest so every listed item starts its own "
198
+ "line; compressed content may follow on that line or the next "
199
+ "line.") in message
159
200
  assert "- general_principles\n Simplest thing that works" in message
160
- assert "does not satisfy coverage" in message
161
- assert "the item must be the only text on its line" in message
201
+ assert "the item alone on the line" not in message
202
+ assert "the only text on its line" not in message
162
203
  assert note in message
163
204
 
164
205
  def test_note_keeps_missing_items_verbatim(self, runtime):
@@ -29,3 +29,11 @@ class TestRenderBriefPrompt:
29
29
  assert "`## Scope`" in prompt
30
30
  assert "`## Risks`" in prompt
31
31
  assert "Do not shorten or rename them" in prompt
32
+
33
+ def test_relaxed_contract_is_stated(self):
34
+ prompt = render_brief_prompt(SECTIONS)
35
+ assert "at the start of its own line" in prompt
36
+ assert "must be reproduced character-exact" in prompt
37
+ assert "never transliterate it" in prompt
38
+ assert "must appear at the start of their own lines" in prompt
39
+ assert "the only text on its line" not in prompt
@@ -56,8 +56,12 @@ def text_with_single_decision(draw):
56
56
 
57
57
  @st.composite
58
58
  def text_with_conflicting_decisions(draw):
59
- first = draw(verdict_line.filter(lambda v: "PASS" in v.split(":", 1)[1]))
60
- second = draw(verdict_line.filter(lambda v: "FAIL" in v.split(":", 1)[1]))
59
+ first = draw(verdict_line.filter(
60
+ lambda v: "PASS" in v.split(":", 1)[1]
61
+ and "FAIL" not in v.split(":", 1)[1]))
62
+ second = draw(verdict_line.filter(
63
+ lambda v: "FAIL" in v.split(":", 1)[1]
64
+ and "PASS" not in v.split(":", 1)[1]))
61
65
  return f"{first}\n{second}"
62
66
 
63
67