verifiers 0.4.1.dev25__tar.gz → 0.4.1.dev27__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 (265) hide show
  1. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/PKG-INFO +1 -1
  2. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/conftest.py +3 -0
  3. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/grid.py +1 -0
  4. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/__init__.py +3 -0
  5. verifiers-0.4.1.dev27/verifiers/v1/harnesses/opencode/__init__.py +6 -0
  6. verifiers-0.4.1.dev27/verifiers/v1/harnesses/opencode/harness.py +141 -0
  7. verifiers-0.4.1.dev27/verifiers/v1/harnesses/opencode/mcp-ready.js +44 -0
  8. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/rollout.py +96 -81
  9. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/.gitignore +0 -0
  10. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/LICENSE +0 -0
  11. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/README.md +0 -0
  12. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/pyproject.toml +0 -0
  13. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/agent_user_harness_v1.py +0 -0
  14. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/agent_user_v1.py +0 -0
  15. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/counter_tool_v1.py +0 -0
  16. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/duet_v1.py +0 -0
  17. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/echo_acp_resume_v1.py +0 -0
  18. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/echo_agentic_v1.py +0 -0
  19. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/echo_tool_v1.py +0 -0
  20. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/echo_user_sim_v1.py +0 -0
  21. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/echo_v1.py +0 -0
  22. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/network_policy_v1.py +0 -0
  23. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/pyproject.toml +0 -0
  24. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/skill_codeword_v1.py +0 -0
  25. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/skills/project-codeword/SKILL.md +0 -0
  26. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/fixtures/tool_response_image_v1.py +0 -0
  27. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_agent_user.py +0 -0
  28. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_agentic_judge.py +0 -0
  29. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_compose_scoring.py +0 -0
  30. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_examples.py +0 -0
  31. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_limits.py +0 -0
  32. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_multi_agent.py +0 -0
  33. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_multi_turn.py +0 -0
  34. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_multi_turn_tool.py +0 -0
  35. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_multimodal_tool.py +0 -0
  36. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_network_policy.py +0 -0
  37. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_resume_tool.py +0 -0
  38. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_rubric_judge.py +0 -0
  39. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_sampling.py +0 -0
  40. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_shared_tool.py +0 -0
  41. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_single_turn.py +0 -0
  42. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_skills.py +0 -0
  43. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_stateful_tool.py +0 -0
  44. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_user_echo.py +0 -0
  45. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/integration/test_user_sim.py +0 -0
  46. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/cli/test_replay.py +0 -0
  47. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/conftest.py +0 -0
  48. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/harnesses/test_core.py +0 -0
  49. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/harnesses/test_rlm.py +0 -0
  50. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/interception/test_tunnel.py +0 -0
  51. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/judges/conftest.py +0 -0
  52. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/judges/test_judge.py +0 -0
  53. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/judges/test_reference.py +0 -0
  54. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/judges/test_rubric.py +0 -0
  55. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/runtimes/test_e2b.py +0 -0
  56. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/serve/test_client.py +0 -0
  57. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/serve/test_delta.py +0 -0
  58. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/serve/test_pool.py +0 -0
  59. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/test_agent.py +0 -0
  60. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/test_dialects.py +0 -0
  61. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/test_graph.py +0 -0
  62. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/test_semantic.py +0 -0
  63. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/test_task.py +0 -0
  64. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/test_taskset.py +0 -0
  65. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/test_trace.py +0 -0
  66. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/utils/test_compile.py +0 -0
  67. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/utils/test_loaders.py +0 -0
  68. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/utils/test_platform.py +0 -0
  69. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/tests/unit/utils/test_score.py +0 -0
  70. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/__init__.py +0 -0
  71. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/__init__.py +0 -0
  72. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/acp/__init__.py +0 -0
  73. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/acp/runner.py +0 -0
  74. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/agent.py +0 -0
  75. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/__init__.py +0 -0
  76. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/dashboard/__init__.py +0 -0
  77. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/dashboard/base.py +0 -0
  78. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/dashboard/eval.py +0 -0
  79. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/dashboard/replay.py +0 -0
  80. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/dashboard/validate.py +0 -0
  81. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/debug.py +0 -0
  82. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/eval/__init__.py +0 -0
  83. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/eval/hint.py +0 -0
  84. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/eval/main.py +0 -0
  85. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/eval/resume.py +0 -0
  86. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/eval/runner.py +0 -0
  87. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/gepa.py +0 -0
  88. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/output.py +0 -0
  89. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/replay.py +0 -0
  90. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/resolve.py +0 -0
  91. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/resume.py +0 -0
  92. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/cli/validate.py +0 -0
  93. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/clients/__init__.py +0 -0
  94. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/clients/base.py +0 -0
  95. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/clients/client.py +0 -0
  96. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/clients/eval.py +0 -0
  97. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/clients/train.py +0 -0
  98. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/__init__.py +0 -0
  99. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/agent.py +0 -0
  100. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/cli/__init__.py +0 -0
  101. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/cli/debug.py +0 -0
  102. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/cli/env.py +0 -0
  103. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/cli/eval.py +0 -0
  104. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/cli/replay.py +0 -0
  105. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/cli/validate.py +0 -0
  106. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/client.py +0 -0
  107. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/env.py +0 -0
  108. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/harness.py +0 -0
  109. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/judge.py +0 -0
  110. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/retries.py +0 -0
  111. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/runtime.py +0 -0
  112. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/scoring.py +0 -0
  113. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/select.py +0 -0
  114. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/serve.py +0 -0
  115. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/task.py +0 -0
  116. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/configs/taskset.py +0 -0
  117. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/dialects/__init__.py +0 -0
  118. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/dialects/anthropic.py +0 -0
  119. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/dialects/base.py +0 -0
  120. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/dialects/chat.py +0 -0
  121. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/dialects/responses.py +0 -0
  122. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/env.py +0 -0
  123. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/envs/__init__.py +0 -0
  124. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
  125. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/envs/agentic_judge/env.py +0 -0
  126. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
  127. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/envs/best_of_n/env.py +0 -0
  128. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/envs/single_agent/__init__.py +0 -0
  129. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/envs/single_agent/env.py +0 -0
  130. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/envs/user_sim/__init__.py +0 -0
  131. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/envs/user_sim/env.py +0 -0
  132. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/episode.py +0 -0
  133. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/errors.py +0 -0
  134. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/gepa/__init__.py +0 -0
  135. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/gepa/adapter.py +0 -0
  136. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/gepa/config.py +0 -0
  137. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/gepa/dataset.py +0 -0
  138. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/gepa/reflection.py +0 -0
  139. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/gepa/runner.py +0 -0
  140. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/grading.py +0 -0
  141. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/graph.py +0 -0
  142. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harness.py +0 -0
  143. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/bash/__init__.py +0 -0
  144. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/bash/harness.py +0 -0
  145. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
  146. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
  147. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/browser_use/program.py +0 -0
  148. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
  149. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/claude_code/harness.py +0 -0
  150. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/codex/__init__.py +0 -0
  151. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/codex/gate.mjs +0 -0
  152. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/codex/harness.py +0 -0
  153. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
  154. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
  155. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
  156. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
  157. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
  158. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
  159. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
  160. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
  161. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/node.py +0 -0
  162. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/null/__init__.py +0 -0
  163. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/null/harness.py +0 -0
  164. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
  165. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
  166. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/pi/__init__.py +0 -0
  167. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/pi/gate.mjs +0 -0
  168. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/pi/harness.py +0 -0
  169. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
  170. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
  171. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
  172. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/rlm/harness.py +0 -0
  173. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
  174. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
  175. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
  176. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/utils/__init__.py +0 -0
  177. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/utils/compaction.py +0 -0
  178. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/utils/core.py +0 -0
  179. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/utils/install.py +0 -0
  180. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/utils/launch.py +0 -0
  181. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/harnesses/utils/mcp.py +0 -0
  182. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/interception/__init__.py +0 -0
  183. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/interception/base.py +0 -0
  184. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/interception/pool.py +0 -0
  185. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/interception/server.py +0 -0
  186. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/interception/tunnel/__init__.py +0 -0
  187. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/interception/tunnel/base.py +0 -0
  188. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/interception/tunnel/custom.py +0 -0
  189. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/interception/tunnel/prime.py +0 -0
  190. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/judge.py +0 -0
  191. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/judges/__init__.py +0 -0
  192. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/judges/decision.py +0 -0
  193. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/judges/reference.py +0 -0
  194. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/judges/reference.txt +0 -0
  195. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/judges/rubric.py +0 -0
  196. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/judges/rubric.txt +0 -0
  197. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/mcp/__init__.py +0 -0
  198. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/mcp/launch.py +0 -0
  199. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/mcp/server.py +0 -0
  200. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/mcp/toolset.py +0 -0
  201. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/__init__.py +0 -0
  202. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/apptainer.py +0 -0
  203. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/base.py +0 -0
  204. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/compose.py +0 -0
  205. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/container.py +0 -0
  206. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/deployment.py +0 -0
  207. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/docker/__init__.py +0 -0
  208. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/docker/egress.py +0 -0
  209. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/e2b.py +0 -0
  210. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/limiters.py +0 -0
  211. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/modal.py +0 -0
  212. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/prime.py +0 -0
  213. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/runtime.py +0 -0
  214. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/strategies.py +0 -0
  215. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/runtimes/subprocess.py +0 -0
  216. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/semantic.py +0 -0
  217. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/serve/__init__.py +0 -0
  218. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/serve/client.py +0 -0
  219. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/serve/delta.py +0 -0
  220. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/serve/encoding.py +0 -0
  221. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/serve/pool.py +0 -0
  222. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/serve/server.py +0 -0
  223. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/serve/types.py +0 -0
  224. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/session.py +0 -0
  225. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/state.py +0 -0
  226. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/task.py +0 -0
  227. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/taskset.py +0 -0
  228. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/__init__.py +0 -0
  229. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
  230. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
  231. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
  232. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
  233. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
  234. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
  235. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
  236. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
  237. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
  238. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
  239. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
  240. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
  241. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/trace.py +0 -0
  242. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/types.py +0 -0
  243. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/__init__.py +0 -0
  244. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/aio.py +0 -0
  245. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/artifacts.py +0 -0
  246. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/compile.py +0 -0
  247. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/decorators.py +0 -0
  248. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/env.py +0 -0
  249. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/eval.py +0 -0
  250. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/format.py +0 -0
  251. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/generic.py +0 -0
  252. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/git.py +0 -0
  253. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/image.py +0 -0
  254. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/interrupt.py +0 -0
  255. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/loaders.py +0 -0
  256. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/logging.py +0 -0
  257. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/memory.py +0 -0
  258. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/paths.py +0 -0
  259. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/platform.py +0 -0
  260. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/prime.py +0 -0
  261. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/retries.py +0 -0
  262. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/scope.py +0 -0
  263. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/score.py +0 -0
  264. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/trace_store.py +0 -0
  265. {verifiers-0.4.1.dev25 → verifiers-0.4.1.dev27}/verifiers/v1/utils/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: verifiers
3
- Version: 0.4.1.dev25
3
+ Version: 0.4.1.dev27
4
4
  Summary: Verifiers: Environments for LLM Reinforcement Learning
5
5
  Project-URL: Homepage, https://github.com/primeintellect-ai/verifiers
6
6
  Project-URL: Documentation, https://github.com/primeintellect-ai/verifiers
@@ -67,6 +67,9 @@ def pytest_configure(config) -> None:
67
67
  inherits `PYTHONPATH` but not pytest's in-process `pythonpath`. Put the fixture dir on
68
68
  `PYTHONPATH` so a fixture server module (e.g. `tool_response_image_v1`)
69
69
  resolves there too — an installed example package (e.g. `glossary`) already would."""
70
+ config.addinivalue_line(
71
+ "markers", "opencode: integration cells on the OpenCode harness"
72
+ )
70
73
  fixtures = str(Path(__file__).parent / "fixtures")
71
74
  existing = os.environ.get("PYTHONPATH", "")
72
75
  if fixtures not in existing.split(os.pathsep):
@@ -52,6 +52,7 @@ HARNESSES: list[str | dict] = [
52
52
  "kimi-code",
53
53
  "pi",
54
54
  "openclaw",
55
+ "opencode",
55
56
  "mini-swe-agent",
56
57
  "prime-agent",
57
58
  ]
@@ -19,6 +19,7 @@ from verifiers.v1.harnesses.mini_swe_agent import (
19
19
  )
20
20
  from verifiers.v1.harnesses.null import NullHarness, NullHarnessConfig
21
21
  from verifiers.v1.harnesses.openclaw import OpenClawHarness, OpenClawHarnessConfig
22
+ from verifiers.v1.harnesses.opencode import OpenCodeHarness, OpenCodeHarnessConfig
22
23
  from verifiers.v1.harnesses.pi import PiHarness, PiHarnessConfig
23
24
  from verifiers.v1.harnesses.prime_agent import (
24
25
  PrimeAgentHarness,
@@ -46,6 +47,8 @@ __all__ = [
46
47
  "NullHarnessConfig",
47
48
  "OpenClawHarness",
48
49
  "OpenClawHarnessConfig",
50
+ "OpenCodeHarness",
51
+ "OpenCodeHarnessConfig",
49
52
  "PiHarness",
50
53
  "PiHarnessConfig",
51
54
  "PrimeAgentHarness",
@@ -0,0 +1,6 @@
1
+ from verifiers.v1.harnesses.opencode.harness import (
2
+ OpenCodeHarness,
3
+ OpenCodeHarnessConfig,
4
+ )
5
+
6
+ __all__ = ["OpenCodeHarness", "OpenCodeHarnessConfig"]
@@ -0,0 +1,141 @@
1
+ """Run OpenCode 2's native ACP server against interception."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Literal
6
+
7
+ from verifiers.v1.acp import ACPConfig, ACPHarness
8
+ from verifiers.v1.clients import ModelContext
9
+ from verifiers.v1.configs.harness import HarnessConfig, PinnedVersion
10
+ from verifiers.v1.harnesses.node import ensure_node
11
+ from verifiers.v1.harnesses.utils.install import ensure_installed, remove_dir
12
+ from verifiers.v1.runtimes import Runtime
13
+ from verifiers.v1.task import TaskData
14
+ from verifiers.v1.trace import Trace
15
+
16
+ OPENCODE_DIR = "/var/tmp/vf-opencode"
17
+ STATE_ROOT = "/tmp/vf-opencode-runs"
18
+ KEY_VAR = "VF_OPENCODE_API_KEY"
19
+ MCP_READY = (Path(__file__).resolve().parent / "mcp-ready.js").read_bytes()
20
+
21
+ INSTALL = r"""
22
+ set -e
23
+ export PATH="/var/tmp/vf-node/bin:$PATH"
24
+ npm install --prefix "$VF_OPENCODE_DIR" --no-audit --no-fund --omit=dev \
25
+ "@opencode/cli@$VF_OPENCODE_VERSION" >/dev/null
26
+ """
27
+
28
+
29
+ class OpenCodeHarnessConfig(HarnessConfig):
30
+ version: PinnedVersion = "2.0.26"
31
+ """OpenCode 2 release to install, pinned for reproducibility."""
32
+ transport: Literal["chat_completions", "responses", "anthropic_messages"] = (
33
+ "chat_completions"
34
+ )
35
+ """Model API transport."""
36
+ disabled_tools: list[str] | None = None
37
+ """OpenCode permission actions to deny, such as shell, read, or edit."""
38
+
39
+
40
+ class OpenCodeHarness(ACPHarness[OpenCodeHarnessConfig]):
41
+ APPENDS_SYSTEM_PROMPT = True
42
+ SUPPORTS_MCP = True
43
+ SUPPORTS_SKILLS = True
44
+
45
+ async def setup(self, runtime: Runtime) -> None:
46
+ await ensure_node(runtime)
47
+ directory = f"{OPENCODE_DIR}/{self.config.version}"
48
+ await ensure_installed(
49
+ runtime,
50
+ directory=directory,
51
+ ready=f"[ -x {directory}/node_modules/.bin/opencode ]",
52
+ install=INSTALL,
53
+ env={
54
+ **self.config.resolved_env,
55
+ "VF_OPENCODE_DIR": directory,
56
+ "VF_OPENCODE_VERSION": self.config.version,
57
+ },
58
+ label="OpenCode",
59
+ )
60
+ await super().setup(runtime)
61
+
62
+ async def prepare_acp(
63
+ self,
64
+ ctx: ModelContext,
65
+ trace: Trace,
66
+ runtime: Runtime,
67
+ endpoint: str,
68
+ secret: str,
69
+ mcp_urls: dict[str, str],
70
+ data: TaskData,
71
+ ) -> ACPConfig:
72
+ root = f"{STATE_ROOT}/{trace.id}"
73
+ config_dir = f"{root}/config/opencode"
74
+ await self.install_skills(runtime, f"{config_dir}/skills")
75
+ system_prompt, prompt = self.resolve_prompt(data)
76
+ if system_prompt:
77
+ await runtime.write(f"{config_dir}/AGENTS.md", system_prompt.encode())
78
+
79
+ package = {
80
+ "chat_completions": "openai-compatible",
81
+ "responses": "openai-compatible/responses",
82
+ "anthropic_messages": "anthropic-compatible",
83
+ }[self.config.transport]
84
+ settings = {
85
+ "model": "intercept/model",
86
+ "providers": {
87
+ "intercept": {
88
+ "env": [KEY_VAR],
89
+ "package": f"@opencode/ai/providers/{package}",
90
+ "settings": {"baseURL": endpoint},
91
+ "models": {"model": {"modelID": ctx.model}},
92
+ }
93
+ },
94
+ "default_agent": "build",
95
+ # Auxiliary title/summary model calls must not become rollout turns.
96
+ "agents": {"title": {"disabled": True}, "summary": {"disabled": True}},
97
+ "permissions": [
98
+ {"action": "*", "resource": "*", "effect": "allow"},
99
+ *(
100
+ {"action": tool, "resource": "*", "effect": "deny"}
101
+ for tool in self.config.disabled_tools or []
102
+ ),
103
+ ],
104
+ "update": "disable",
105
+ "share": "disabled",
106
+ "snapshots": False,
107
+ }
108
+ if mcp_urls:
109
+ plugin_dir = f"{root}/mcp-ready"
110
+ await runtime.write(f"{plugin_dir}/index.js", MCP_READY)
111
+ settings["plugins"] = [
112
+ {
113
+ "package": plugin_dir,
114
+ "options": {
115
+ "servers": list(mcp_urls),
116
+ "timeout": int(self.config.tool_timeout * 1000),
117
+ },
118
+ }
119
+ ]
120
+ return ACPConfig(
121
+ command=[
122
+ f"{OPENCODE_DIR}/{self.config.version}/node_modules/.bin/opencode",
123
+ "acp",
124
+ ],
125
+ env={
126
+ **self.config.resolved_env,
127
+ KEY_VAR: secret,
128
+ "XDG_CONFIG_HOME": f"{root}/config",
129
+ "XDG_DATA_HOME": f"{root}/data",
130
+ "XDG_CACHE_HOME": f"{root}/cache",
131
+ "XDG_STATE_HOME": f"{root}/state",
132
+ "OPENCODE_CONFIG_DIR": config_dir,
133
+ "OPENCODE_CONFIG_CONTENT": json.dumps(settings),
134
+ "OPENCODE_DISABLE_AUTOUPDATE": "1",
135
+ "OPENCODE_DISABLE_MODELS_FETCH": "1",
136
+ },
137
+ prompt=prompt,
138
+ )
139
+
140
+ async def cleanup(self, trace: Trace, runtime: Runtime) -> None:
141
+ await remove_dir(runtime, f"{STATE_ROOT}/{trace.id}", "OpenCode state")
@@ -0,0 +1,44 @@
1
+ import { Context, Effect } from "effect";
2
+
3
+ export default {
4
+ id: "verifiers.mcp-ready",
5
+ effect: Effect.fn(function* (ctx) {
6
+ // ACP registrations bypass OpenCode's configured MCP server timeout defaults.
7
+ yield* ctx.mcp.transform((editor) => {
8
+ for (const name of ctx.options.servers) {
9
+ editor.update(name, (server) => {
10
+ server.timeout = { ...server.timeout, execution: ctx.options.timeout };
11
+ });
12
+ }
13
+ });
14
+ yield* ctx.session.hook(
15
+ "prompt",
16
+ Effect.fn(function* () {
17
+ // OpenCode 2.0.26's public plugin API omits discovered MCP tools. Read
18
+ // its native service in the prompt's location context, including empty servers.
19
+ const mcp = yield* Context.Service()("@opencode/MCP");
20
+ const servers = yield* mcp.servers();
21
+ for (const name of ctx.options.servers) {
22
+ const server = servers.find((server) => server.name === name);
23
+ if (server?.status.status !== "connected") {
24
+ return yield* Effect.die(
25
+ new Error(`MCP server ${name} is not connected: ${JSON.stringify(server?.status)}`),
26
+ );
27
+ }
28
+ }
29
+ const expected = (yield* mcp.tools())
30
+ .filter((tool) => ctx.options.servers.includes(tool.server))
31
+ .map((tool) => `${tool.server}_${tool.name}`.replace(/[^a-zA-Z0-9_-]/g, "_"));
32
+ // ACP session/new awaits connection, but OpenCode refreshes its tool
33
+ // registry asynchronously. Do not let the prompt snapshot an old catalog.
34
+ yield* Effect.gen(function* () {
35
+ while (true) {
36
+ const registered = new Set((yield* ctx.tool.list()).map((tool) => tool.id));
37
+ if (expected.every((name) => registered.has(name))) return;
38
+ yield* Effect.sleep("10 millis");
39
+ }
40
+ }).pipe(Effect.timeout(ctx.options.timeout), Effect.orDie);
41
+ }),
42
+ );
43
+ }),
44
+ };
@@ -648,10 +648,61 @@ class Rollout:
648
648
  }
649
649
  logger.warning("harness scoring failed", exc_info=True)
650
650
 
651
+ async def _close_harness(self) -> None:
652
+ """Close the harness session, then the tool servers and interception. Idempotent.
653
+
654
+ Generation has already completed, so a teardown failure is logged rather than
655
+ raised: it must not discard an otherwise scoreable (or already scored)
656
+ trajectory."""
657
+ if self._harness_session is not None:
658
+ try:
659
+ await self._harness_session.close()
660
+ except Exception:
661
+ logger.warning(
662
+ "harness session close failed (rollout %s)",
663
+ self.trace.id,
664
+ exc_info=True,
665
+ )
666
+ try:
667
+ await self._stack.aclose()
668
+ except Exception:
669
+ logger.warning(
670
+ "tool server or interception teardown failed (rollout %s)",
671
+ self.trace.id,
672
+ exc_info=True,
673
+ )
674
+
675
+ def _agent_finished(self) -> bool:
676
+ """Whether the agent ended its last segment on its own, or waits between the
677
+ segments of an exchange: nothing it runs is still acting on the box."""
678
+ return not self._failed and self.trace.stop_condition in (
679
+ None,
680
+ "agent_completed",
681
+ )
682
+
683
+ def _metrics_deadline(self) -> float:
684
+ """Best-effort metrics get at most 30 seconds, and a tenth of a finite scoring budget."""
685
+ budget = (
686
+ min(30.0, self._timeouts.scoring / 10)
687
+ if self._timeouts.scoring is not None
688
+ else 30.0
689
+ )
690
+ return asyncio.get_running_loop().time() + budget
691
+
651
692
  async def close(self) -> Trace:
652
- """Finish the rollout: tool servers and interception down, task `finalize`
653
- and per-rollout scoring (skipped when the run already failed — but a stopped
654
- run is complete and scores its partial trajectory), then runtime teardown.
693
+ """Finish the rollout: task `finalize` and per-rollout scoring (skipped when the
694
+ run already failed, but a stopped run is complete and scores its partial
695
+ trajectory), the harness session, tool servers and interception, then runtime
696
+ teardown.
697
+
698
+ An agent that finished its turn keeps its harness session open while its
699
+ environment is collected and, when grading runs in the agent's own box, graded:
700
+ harnesses stop the processes their commands started when the session closes,
701
+ and a service the task asked the agent to leave running is part of what gets
702
+ graded. A run stopped mid-turn (a timeout or a stop condition) closes the
703
+ session first, so an agent that may still be acting cannot change the box while
704
+ it is graded. Isolated grading runs in its own box after the session closes.
705
+ Harness metrics run after the close, which hands over the session's final state.
655
706
  Idempotent; always returns the trace."""
656
707
  if self._closed:
657
708
  return self.trace
@@ -659,67 +710,29 @@ class Rollout:
659
710
  trace = self.trace
660
711
  runtime = self.runtime
661
712
  try:
662
- if self._harness_session is not None:
663
- try:
664
- await self._harness_session.close()
665
- except Exception:
666
- # Generation already completed. A transport teardown failure
667
- # must not discard its otherwise scoreable trajectory.
668
- logger.warning(
669
- "harness session close failed (rollout %s)",
670
- trace.id,
671
- exc_info=True,
672
- )
673
- try:
674
- await self._stack.aclose()
675
- finally:
676
- if trace.timing.agent.start and not trace.timing.agent.end:
677
- trace.timing.agent.end = time.time()
678
- trace.notify()
713
+ if trace.timing.agent.start and not trace.timing.agent.end:
714
+ trace.timing.agent.end = time.time()
715
+ if not self._agent_finished():
716
+ await self._close_harness()
717
+ trace.notify()
679
718
  if not self._failed and self._opened:
680
719
  assert runtime is not None
681
720
  assert self.deployment is not None
682
721
  grader = self._grader
683
722
  isolated = self._scoring_policy.mode == "isolated"
723
+ loop = asyncio.get_running_loop()
684
724
  trace.timing.finalize.start = time.time()
725
+ finalize_deadline = (
726
+ loop.time() + self._timeouts.finalize
727
+ if self._timeouts.finalize is not None
728
+ else None
729
+ )
685
730
  scoring_elapsed = 0.0
686
- early_metrics = (
687
- isolated and grader is not None
688
- ) or self._on_score is not None
689
- if early_metrics:
690
- scoring_started = asyncio.get_running_loop().time()
691
- # Metrics are best-effort: reserve at least 90% of a finite
692
- # scoring budget for verification, with a 30-second ceiling.
693
- # Capture solver metrics before stopping its workspace or letting
694
- # an agentic judge change files and execution state in place.
695
- try:
696
- async with (
697
- asyncio.timeout(
698
- min(30.0, self._timeouts.scoring / 10)
699
- if self._timeouts.scoring is not None
700
- else 30.0
701
- ),
702
- boundary(HarnessError, "harness scoring"),
703
- ):
704
- await self.harness.score(
705
- trace,
706
- self._agent_runtime or runtime,
707
- )
708
- except Exception as error:
709
- # Metrics are best-effort; their failure must not prevent
710
- # collecting the submission and running its fresh verifier.
711
- trace.info["harness_scoring_error"] = {
712
- "type": type(error).__name__,
713
- "message": str(error),
714
- }
715
- logger.warning("harness scoring failed", exc_info=True)
716
- scoring_elapsed = (
717
- asyncio.get_running_loop().time() - scoring_started
718
- )
731
+ metrics_done = False
719
732
  finalize_phase = "task finalization"
720
733
  try:
721
734
  async with (
722
- asyncio.timeout(self._timeouts.finalize),
735
+ asyncio.timeout_at(finalize_deadline),
723
736
  boundary(TaskError, "task finalize"),
724
737
  ):
725
738
  await invoke(
@@ -739,8 +752,24 @@ class Rollout:
739
752
  self.task.data.artifacts,
740
753
  max_bytes=self.task.data.artifact_max_bytes,
741
754
  )
742
- if grader is not None:
743
- finalize_phase = "service finalization"
755
+ if grader is not None:
756
+ # The submission is collected: close the solver's session, then
757
+ # capture its metrics before the workspace stops and the verifier
758
+ # starts. Neither counts against finalization; the metrics count
759
+ # against scoring, as they did when they ran first.
760
+ interlude_started = loop.time()
761
+ await self._close_harness()
762
+ metrics_started = loop.time()
763
+ await self._score_harness(runtime, self._metrics_deadline())
764
+ metrics_done = True
765
+ scoring_elapsed = loop.time() - metrics_started
766
+ if finalize_deadline is not None:
767
+ finalize_deadline += loop.time() - interlude_started
768
+ finalize_phase = "service finalization"
769
+ async with (
770
+ asyncio.timeout_at(finalize_deadline),
771
+ boundary(TaskError, "task finalize"),
772
+ ):
744
773
  await self._cleanup_harness(runtime)
745
774
  await self.deployment.stop_execution()
746
775
  await self.task.finalize_services(
@@ -753,15 +782,9 @@ class Rollout:
753
782
  except TimeoutError:
754
783
  self.timeout("finalize")
755
784
  if isolated:
756
- if not early_metrics:
757
- # Deferred scoring has not captured metrics yet.
758
- deadline = (
759
- asyncio.get_running_loop().time()
760
- + self._timeouts.scoring
761
- if self._timeouts.scoring is not None
762
- else None
763
- )
764
- await self._score_harness(runtime, deadline)
785
+ await self._close_harness()
786
+ if not metrics_done:
787
+ await self._score_harness(runtime, self._metrics_deadline())
765
788
  raise TaskError(
766
789
  f"{finalize_phase} exceeded its finalize deadline"
767
790
  ) from None
@@ -770,26 +793,12 @@ class Rollout:
770
793
  trace.timing.scoring.start = now
771
794
  trace.notify()
772
795
  scoring_deadline = (
773
- asyncio.get_running_loop().time()
774
- + max(0.0, self._timeouts.scoring - scoring_elapsed)
796
+ loop.time() + max(0.0, self._timeouts.scoring - scoring_elapsed)
775
797
  if self._timeouts.scoring is not None
776
798
  else None
777
799
  )
778
- if not early_metrics:
779
- # Collect metrics before a shared verifier can exhaust the budget.
780
- metrics_budget = (
781
- min(30.0, self._timeouts.scoring / 10)
782
- if self._timeouts.scoring is not None
783
- else 30.0
784
- )
785
- await self._score_harness(
786
- runtime, asyncio.get_running_loop().time() + metrics_budget
787
- )
788
800
  try:
789
- if (
790
- scoring_deadline is not None
791
- and asyncio.get_running_loop().time() >= scoring_deadline
792
- ):
801
+ if scoring_deadline is not None and loop.time() >= scoring_deadline:
793
802
  raise TimeoutError("scoring budget exhausted")
794
803
  async with (
795
804
  asyncio.timeout_at(
@@ -847,6 +856,12 @@ class Rollout:
847
856
  self.fail(
848
857
  TaskError("verification exceeded its scoring deadline")
849
858
  )
859
+ finally:
860
+ if grader is None:
861
+ # Graded in the agent's own box: now the session can close, and
862
+ # the metrics read what it handed over and left behind.
863
+ await self._close_harness()
864
+ await self._score_harness(runtime, self._metrics_deadline())
850
865
  trace.timing.scoring.end = time.time()
851
866
  trace.notify()
852
867
  except Exception as e: # noqa: BLE001 - finalize boundary records every rollout failure
File without changes