verifiers 0.3.2.dev171__tar.gz → 0.3.2.dev185__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 (226) hide show
  1. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/PKG-INFO +1 -1
  2. verifiers-0.3.2.dev185/tests/v1/fixtures/delivery_behaviors.py +196 -0
  3. verifiers-0.3.2.dev185/tests/v1/test_delivery_behaviors.py +288 -0
  4. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/__init__.py +4 -0
  5. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/acp/__init__.py +67 -9
  6. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/acp/runner.py +58 -29
  7. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/agent.py +276 -7
  8. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/client.py +4 -1
  9. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/judge.py +2 -2
  10. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harness.py +6 -0
  11. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/server.py +3 -0
  12. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judges/__init__.py +3 -0
  13. verifiers-0.3.2.dev185/verifiers/v1/judges/decision.py +105 -0
  14. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/rollout.py +15 -1
  15. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/session.py +2 -0
  16. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/.gitignore +0 -0
  17. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/LICENSE +0 -0
  18. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/README.md +0 -0
  19. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/pyproject.toml +0 -0
  20. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/conftest.py +0 -0
  21. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/agent_user_v1.py +0 -0
  22. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/counter_tool_v1.py +0 -0
  23. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/duet_v1.py +0 -0
  24. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/echo_acp_resume_v1.py +0 -0
  25. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/echo_agentic_v1.py +0 -0
  26. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/echo_tool_v1.py +0 -0
  27. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/echo_user_sim_v1.py +0 -0
  28. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/echo_v1.py +0 -0
  29. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/pyproject.toml +0 -0
  30. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/fixtures/tool_response_image_v1.py +0 -0
  31. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_color_codeword_tasks.py +0 -0
  32. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_configs.py +0 -0
  33. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_e2e.py +0 -0
  34. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_envs.py +0 -0
  35. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_graph.py +0 -0
  36. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_judges.py +0 -0
  37. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_platform_run.py +0 -0
  38. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_scoring.py +0 -0
  39. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_serve_delta.py +0 -0
  40. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_taskset.py +0 -0
  41. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/tests/v1/test_trace.py +0 -0
  42. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/__init__.py +0 -0
  43. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/__init__.py +0 -0
  44. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/dashboard/__init__.py +0 -0
  45. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/dashboard/base.py +0 -0
  46. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/dashboard/eval.py +0 -0
  47. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/dashboard/replay.py +0 -0
  48. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/dashboard/validate.py +0 -0
  49. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/debug.py +0 -0
  50. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/eval/__init__.py +0 -0
  51. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/eval/hint.py +0 -0
  52. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/eval/main.py +0 -0
  53. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/eval/resume.py +0 -0
  54. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/eval/runner.py +0 -0
  55. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/gepa.py +0 -0
  56. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/init.py +0 -0
  57. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/output.py +0 -0
  58. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/replay.py +0 -0
  59. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/resolve.py +0 -0
  60. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/resume.py +0 -0
  61. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/cli/validate.py +0 -0
  62. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/clients/__init__.py +0 -0
  63. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/clients/base.py +0 -0
  64. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/clients/client.py +0 -0
  65. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/clients/eval.py +0 -0
  66. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/clients/train.py +0 -0
  67. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/__init__.py +0 -0
  68. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/agent.py +0 -0
  69. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/__init__.py +0 -0
  70. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/debug.py +0 -0
  71. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/env.py +0 -0
  72. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/eval.py +0 -0
  73. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/init.py +0 -0
  74. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/replay.py +0 -0
  75. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/cli/validate.py +0 -0
  76. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/env.py +0 -0
  77. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/harness.py +0 -0
  78. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/retries.py +0 -0
  79. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/runtime.py +0 -0
  80. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/select.py +0 -0
  81. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/serve.py +0 -0
  82. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/task.py +0 -0
  83. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/configs/taskset.py +0 -0
  84. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/dialects/__init__.py +0 -0
  85. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/dialects/anthropic.py +0 -0
  86. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/dialects/base.py +0 -0
  87. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/dialects/chat.py +0 -0
  88. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/dialects/responses.py +0 -0
  89. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/env.py +0 -0
  90. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/__init__.py +0 -0
  91. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
  92. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/agentic_judge/env.py +0 -0
  93. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
  94. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/best_of_n/env.py +0 -0
  95. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/isolated_verifier/__init__.py +0 -0
  96. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/isolated_verifier/env.py +0 -0
  97. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/shared_agentic_judge/__init__.py +0 -0
  98. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/single_agent/__init__.py +0 -0
  99. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/single_agent/env.py +0 -0
  100. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/user_sim/__init__.py +0 -0
  101. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/envs/user_sim/env.py +0 -0
  102. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/episode.py +0 -0
  103. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/errors.py +0 -0
  104. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/__init__.py +0 -0
  105. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/adapter.py +0 -0
  106. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/config.py +0 -0
  107. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/dataset.py +0 -0
  108. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/reflection.py +0 -0
  109. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/gepa/runner.py +0 -0
  110. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/graph.py +0 -0
  111. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/__init__.py +0 -0
  112. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/bash/__init__.py +0 -0
  113. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/bash/harness.py +0 -0
  114. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
  115. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
  116. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/browser_use/program.py +0 -0
  117. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
  118. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/claude_code/harness.py +0 -0
  119. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/codex/__init__.py +0 -0
  120. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/codex/gate.mjs +0 -0
  121. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/codex/harness.py +0 -0
  122. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
  123. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
  124. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
  125. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
  126. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
  127. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
  128. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
  129. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
  130. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/node.py +0 -0
  131. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/null/__init__.py +0 -0
  132. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/null/harness.py +0 -0
  133. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
  134. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
  135. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/pi/__init__.py +0 -0
  136. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/pi/gate.mjs +0 -0
  137. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/pi/harness.py +0 -0
  138. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
  139. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
  140. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
  141. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/rlm/harness.py +0 -0
  142. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
  143. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
  144. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
  145. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/__init__.py +0 -0
  146. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/compaction.py +0 -0
  147. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/core.py +0 -0
  148. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/install.py +0 -0
  149. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/launch.py +0 -0
  150. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/harnesses/utils/mcp.py +0 -0
  151. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/__init__.py +0 -0
  152. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/base.py +0 -0
  153. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/pool.py +0 -0
  154. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/tunnel/__init__.py +0 -0
  155. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/tunnel/base.py +0 -0
  156. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/tunnel/custom.py +0 -0
  157. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/interception/tunnel/prime.py +0 -0
  158. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judge.py +0 -0
  159. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judges/reference.py +0 -0
  160. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judges/reference.txt +0 -0
  161. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judges/rubric.py +0 -0
  162. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/judges/rubric.txt +0 -0
  163. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/mcp/__init__.py +0 -0
  164. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/mcp/launch.py +0 -0
  165. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/mcp/server.py +0 -0
  166. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/mcp/toolset.py +0 -0
  167. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/__init__.py +0 -0
  168. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/apptainer.py +0 -0
  169. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/base.py +0 -0
  170. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/container.py +0 -0
  171. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/docker/__init__.py +0 -0
  172. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/docker/egress.py +0 -0
  173. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/e2b.py +0 -0
  174. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/limiters.py +0 -0
  175. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/modal.py +0 -0
  176. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/prime.py +0 -0
  177. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/runtimes/subprocess.py +0 -0
  178. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/semantic.py +0 -0
  179. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/__init__.py +0 -0
  180. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/client.py +0 -0
  181. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/delta.py +0 -0
  182. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/encoding.py +0 -0
  183. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/pool.py +0 -0
  184. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/server.py +0 -0
  185. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/serve/types.py +0 -0
  186. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/state.py +0 -0
  187. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/task.py +0 -0
  188. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/taskset.py +0 -0
  189. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/__init__.py +0 -0
  190. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
  191. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/harbor/compose.py +0 -0
  192. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/harbor/env.py +0 -0
  193. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
  194. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
  195. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
  196. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
  197. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
  198. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
  199. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
  200. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
  201. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
  202. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
  203. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
  204. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/trace.py +0 -0
  205. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/types.py +0 -0
  206. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/__init__.py +0 -0
  207. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/aio.py +0 -0
  208. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/artifacts.py +0 -0
  209. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/compile.py +0 -0
  210. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/decorators.py +0 -0
  211. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/format.py +0 -0
  212. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/generic.py +0 -0
  213. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/git.py +0 -0
  214. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/image.py +0 -0
  215. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/interrupt.py +0 -0
  216. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/loaders.py +0 -0
  217. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/logging.py +0 -0
  218. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/memory.py +0 -0
  219. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/paths.py +0 -0
  220. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/platform.py +0 -0
  221. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/prime.py +0 -0
  222. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/retries.py +0 -0
  223. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/scope.py +0 -0
  224. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/score.py +0 -0
  225. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/trace_store.py +0 -0
  226. {verifiers-0.3.2.dev171 → verifiers-0.3.2.dev185}/verifiers/v1/utils/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: verifiers
3
- Version: 0.3.2.dev171
3
+ Version: 0.3.2.dev185
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
@@ -0,0 +1,196 @@
1
+ """Scripted inference and event log for the real-harness delivery examples."""
2
+
3
+ import asyncio
4
+ import json
5
+ import time
6
+ from dataclasses import dataclass, field
7
+
8
+ from starlette.responses import JSONResponse, StreamingResponse
9
+
10
+ LABELS = ("MESSAGE_ALPHA", "MESSAGE_BETA")
11
+
12
+
13
+ def labels(value):
14
+ text = json.dumps(value)
15
+ return sorted((label for label in LABELS if label in text), key=text.index)
16
+
17
+
18
+ @dataclass
19
+ class Scenario:
20
+ harness: str
21
+ state: str
22
+ events: list = field(default_factory=list)
23
+ calls: list = field(default_factory=list)
24
+ model_entered: asyncio.Event = field(default_factory=asyncio.Event)
25
+ model_release: asyncio.Event = field(default_factory=asyncio.Event)
26
+ tool_entered: asyncio.Event = field(default_factory=asyncio.Event)
27
+ tool_release: asyncio.Event = field(default_factory=asyncio.Event)
28
+ response_sent: asyncio.Event = field(default_factory=asyncio.Event)
29
+ endpoint: str = ""
30
+ hold_call: int = 1
31
+
32
+ def record(self, event, **data):
33
+ self.events.append({"index": len(self.events), "event": event, **data})
34
+
35
+ async def barrier(self, request):
36
+ self.record("tool_started")
37
+ self.tool_entered.set()
38
+ await self.tool_release.wait()
39
+ self.record("tool_finished")
40
+ return JSONResponse({"result": "work finished"})
41
+
42
+ async def respond(self, request):
43
+ data = await request.json()
44
+ number = len(self.calls) + 1
45
+ visible = labels(data.get("messages", data.get("input", [])))
46
+ self.calls.append({"number": number, "messages": visible})
47
+ self.record("model_request", call=number, messages=visible)
48
+ if number == self.hold_call:
49
+ self.model_entered.set()
50
+ text = "Observed: " + (", ".join(visible) or "opening prompt") + "."
51
+ tool = None
52
+ if number == 1 and self.state in ("tool", "wait"):
53
+ if self.state == "wait":
54
+ tool = ("wait", {"timeout": 2})
55
+ elif self.harness == "rlm":
56
+ tool = (
57
+ "ipython",
58
+ {
59
+ "code": f"import urllib.request\nprint(urllib.request.urlopen('{self.endpoint}/barrier').read().decode())"
60
+ },
61
+ )
62
+ else:
63
+ tool = (
64
+ "Bash",
65
+ {
66
+ "command": f"curl -fsS {self.endpoint}/barrier",
67
+ "description": "Wait for the demonstration work to finish",
68
+ },
69
+ )
70
+ if self.harness == "rlm":
71
+ if number == self.hold_call and self.state in (
72
+ "model",
73
+ "idle",
74
+ "idle-burst",
75
+ ):
76
+ await self.model_release.wait()
77
+ message = {"role": "assistant", "content": text}
78
+ if tool:
79
+ message = {
80
+ "role": "assistant",
81
+ "content": None,
82
+ "tool_calls": [
83
+ {
84
+ "id": f"tool_{number}",
85
+ "type": "function",
86
+ "function": {
87
+ "name": tool[0],
88
+ "arguments": json.dumps(tool[1]),
89
+ },
90
+ }
91
+ ],
92
+ }
93
+ self.record("model_response", call=number, tool=tool[0] if tool else None)
94
+ if number == 1:
95
+ self.response_sent.set()
96
+ return JSONResponse(
97
+ {
98
+ "id": f"chatcmpl-{number}",
99
+ "object": "chat.completion",
100
+ "created": int(time.time()),
101
+ "model": data["model"],
102
+ "choices": [
103
+ {
104
+ "index": 0,
105
+ "message": message,
106
+ "finish_reason": "tool_calls" if tool else "stop",
107
+ }
108
+ ],
109
+ "usage": {
110
+ "prompt_tokens": 100,
111
+ "completion_tokens": 10,
112
+ "total_tokens": 110,
113
+ },
114
+ }
115
+ )
116
+
117
+ async def stream():
118
+ def event(kind, **fields):
119
+ return (
120
+ "event: "
121
+ + kind
122
+ + "\ndata: "
123
+ + json.dumps({"type": kind, **fields})
124
+ + "\n\n"
125
+ )
126
+
127
+ yield event(
128
+ "message_start",
129
+ message={
130
+ "id": f"msg_{number}",
131
+ "type": "message",
132
+ "role": "assistant",
133
+ "model": data["model"],
134
+ "content": [],
135
+ "stop_reason": None,
136
+ "stop_sequence": None,
137
+ "usage": {"input_tokens": 100, "output_tokens": 0},
138
+ },
139
+ )
140
+ try:
141
+ if number == self.hold_call and self.state in (
142
+ "model",
143
+ "idle",
144
+ "idle-burst",
145
+ ):
146
+ await self.model_release.wait()
147
+ if tool:
148
+ yield event(
149
+ "content_block_start",
150
+ index=0,
151
+ content_block={
152
+ "type": "tool_use",
153
+ "id": f"tool_{number}",
154
+ "name": tool[0],
155
+ "input": {},
156
+ },
157
+ )
158
+ yield event(
159
+ "content_block_delta",
160
+ index=0,
161
+ delta={
162
+ "type": "input_json_delta",
163
+ "partial_json": json.dumps(tool[1]),
164
+ },
165
+ )
166
+ else:
167
+ yield event(
168
+ "content_block_start",
169
+ index=0,
170
+ content_block={"type": "text", "text": ""},
171
+ )
172
+ yield event(
173
+ "content_block_delta",
174
+ index=0,
175
+ delta={"type": "text_delta", "text": text},
176
+ )
177
+ yield event("content_block_stop", index=0)
178
+ yield event(
179
+ "message_delta",
180
+ delta={
181
+ "stop_reason": "tool_use" if tool else "end_turn",
182
+ "stop_sequence": None,
183
+ },
184
+ usage={"output_tokens": 10},
185
+ )
186
+ yield event("message_stop")
187
+ self.record(
188
+ "model_response", call=number, tool=tool[0] if tool else None
189
+ )
190
+ if number == 1:
191
+ self.response_sent.set()
192
+ except asyncio.CancelledError:
193
+ self.record("model_stream_cancelled", call=number)
194
+ raise
195
+
196
+ return StreamingResponse(stream(), media_type="text/event-stream")
@@ -0,0 +1,288 @@
1
+ """Executable delivery examples using real harness processes and scripted inference.
2
+
3
+ Put `rlm`, `claude-agent-acp`, `claude`, and `node` on PATH, then run:
4
+ VF_DELIVERY_BEHAVIORS=1 uv run pytest tests/v1/test_delivery_behaviors.py -v -s
5
+
6
+ For nano-rlm alone, add `-k rlm`; neither subset needs PRIME_API_KEY.
7
+ VF_DELIVERY_OUTPUT optionally selects a directory for timeline.json and normal
8
+ Verifiers traces.jsonl files (one run per case). No model API key is required.
9
+ `idle` sends A after the opening turn finishes, then B during A's resumed work.
10
+ `idle-burst` submits A and B together after the opening turn finishes.
11
+ Other cases submit A and B during the same held model/tool operation. `queue`
12
+ means an ordinary user message deferred until the current turn yields.
13
+ """
14
+
15
+ import asyncio
16
+ import json
17
+ import os
18
+ import shutil
19
+ import socket
20
+ from pathlib import Path
21
+
22
+ import pytest
23
+ import uvicorn
24
+ from delivery_behaviors import LABELS, Scenario, labels
25
+ from starlette.applications import Starlette
26
+ from starlette.routing import Route
27
+
28
+ import verifiers.v1 as vf
29
+ from verifiers.v1.acp import ACPHarness
30
+ from verifiers.v1.episode import Episode, EvalRunInfo
31
+ from verifiers.v1.harnesses.claude_code.harness import (
32
+ ClaudeCodeHarness,
33
+ ClaudeCodeHarnessConfig,
34
+ )
35
+ from verifiers.v1.harnesses.rlm.harness import RLMHarness, RLMHarnessConfig
36
+
37
+ pytestmark = [
38
+ pytest.mark.asyncio,
39
+ pytest.mark.skipif(
40
+ os.environ.get("VF_DELIVERY_BEHAVIORS") != "1",
41
+ reason="opt-in real-harness behavioral examples",
42
+ ),
43
+ ]
44
+
45
+
46
+ @pytest.mark.parametrize("kind", ["rlm", "claude-code"])
47
+ @pytest.mark.parametrize("state", ["idle-burst", "idle", "model", "tool", "wait"])
48
+ @pytest.mark.parametrize(
49
+ "modes",
50
+ [("steer", "steer"), ("steer", "queue"), ("queue", "steer"), ("queue", "queue")],
51
+ ids=["steer-steer", "steer-message", "message-steer", "message-message"],
52
+ )
53
+ async def test_delivery_behavior(kind, state, modes, tmp_path, monkeypatch):
54
+ if state == "wait" and kind != "rlm":
55
+ pytest.skip("wait is a nano-rlm native tool")
56
+ commands = ["rlm"] if kind == "rlm" else ["node", "claude-agent-acp", "claude"]
57
+ bins = {command: shutil.which(command) for command in commands}
58
+ if not all(bins.values()):
59
+ pytest.fail(f"install these harness executables first: {commands}")
60
+ scenario = Scenario(kind, state, hold_call=2 if state.startswith("idle") else 1)
61
+ sock = socket.socket()
62
+ sock.bind(("127.0.0.1", 0))
63
+ scenario.endpoint = f"http://127.0.0.1:{sock.getsockname()[1]}"
64
+ server = uvicorn.Server(
65
+ uvicorn.Config(
66
+ Starlette(
67
+ routes=[
68
+ Route("/v1/chat/completions", scenario.respond, methods=["POST"]),
69
+ Route("/v1/messages", scenario.respond, methods=["POST"]),
70
+ Route("/barrier", scenario.barrier),
71
+ ]
72
+ ),
73
+ log_level="error",
74
+ )
75
+ )
76
+ serving = asyncio.create_task(server.serve(sockets=[sock]))
77
+ monkeypatch.setenv("DELIVERY_SCRIPTED_KEY", "local-scripted")
78
+ base = RLMHarness if kind == "rlm" else ClaudeCodeHarness
79
+
80
+ class LocalHarness(base):
81
+ NEEDS_CONTAINER = False
82
+
83
+ async def setup(self, runtime):
84
+ await ACPHarness.setup(self, runtime)
85
+
86
+ async def prepare_acp(self, *args, **kwargs):
87
+ config = await super().prepare_acp(*args, **kwargs)
88
+ if kind == "rlm":
89
+ config.command = [bins["rlm"], "--acp"]
90
+ else:
91
+ config.command = [bins["node"], bins["claude-agent-acp"]]
92
+ config.env["CLAUDE_CODE_EXECUTABLE"] = bins["claude"]
93
+ return config
94
+
95
+ config = (
96
+ RLMHarnessConfig(id="rlm", builtin_skills=[], max_depth=0, compaction=False)
97
+ if kind == "rlm"
98
+ else ClaudeCodeHarnessConfig(id="claude-code")
99
+ )
100
+ agent = vf.make_agent(
101
+ vf.AgentConfig(
102
+ harness=config,
103
+ model="scripted-delivery" if kind == "rlm" else "claude-sonnet-4-6",
104
+ client={
105
+ "type": "eval",
106
+ "base_url": scenario.endpoint + "/v1",
107
+ "api_key_var": "DELIVERY_SCRIPTED_KEY",
108
+ },
109
+ runtime={"type": "subprocess"},
110
+ max_turns=8,
111
+ timeout={"rollout": 45},
112
+ )
113
+ )
114
+ agent.harness = LocalHarness(config)
115
+ name = f"delivery-{kind}-{state}-{'-'.join(modes)}"
116
+ task = vf.Task(
117
+ vf.TaskData(
118
+ idx=0,
119
+ name=name,
120
+ prompt="Report which incoming messages you have received.",
121
+ system_prompt="Behavioral demonstration with scripted inference. Each reply states which messages are visible in that model request.",
122
+ )
123
+ )
124
+ sends = []
125
+ opening = None
126
+ try:
127
+ async with asyncio.timeout(50), agent, agent.interaction(task) as interaction:
128
+ real_step = interaction._run.step
129
+
130
+ async def step(messages=None, **kwargs):
131
+ scenario.record(
132
+ "turn_started",
133
+ messages=labels(
134
+ [m.model_dump() for m in messages] if messages else []
135
+ ),
136
+ )
137
+ result = await real_step(messages, **kwargs)
138
+ scenario.record("turn_yielded")
139
+ return result
140
+
141
+ interaction._run.step = step
142
+
143
+ async def send(index):
144
+ scenario.record("submitted", message=LABELS[index], mode=modes[index])
145
+ receipt = await interaction.send(
146
+ LABELS[index], mode=modes[index], message_id=LABELS[index]
147
+ )
148
+ scenario.record("receipt", message=LABELS[index], **receipt)
149
+ return receipt
150
+
151
+ opening = asyncio.create_task(interaction.turn())
152
+ if state == "idle-burst":
153
+ await opening
154
+ scenario.record("agent_idle")
155
+ sends.extend(asyncio.create_task(send(i)) for i in range(2))
156
+ await scenario.model_entered.wait()
157
+ elif state == "idle":
158
+ await opening
159
+ scenario.record("agent_idle")
160
+ sends.append(asyncio.create_task(send(0)))
161
+ # A sequential caller can await the wake-up receipt and still
162
+ # steer the next message into the held, resumed turn.
163
+ receipt = await sends[0]
164
+ assert (
165
+ await interaction.send(
166
+ LABELS[0], mode=modes[0], message_id=LABELS[0]
167
+ )
168
+ == receipt
169
+ )
170
+ await scenario.model_entered.wait()
171
+ assert not scenario.model_release.is_set()
172
+ assert not any(
173
+ e["event"] == "turn_yielded"
174
+ for e in scenario.events[
175
+ next(
176
+ e["index"]
177
+ for e in scenario.events
178
+ if e["event"] == "agent_idle"
179
+ )
180
+ + 1 :
181
+ ]
182
+ )
183
+ sends.append(asyncio.create_task(send(1)))
184
+ else:
185
+ if state == "model":
186
+ await scenario.model_entered.wait()
187
+ elif state == "tool":
188
+ await scenario.tool_entered.wait()
189
+ else:
190
+ await scenario.response_sent.wait()
191
+ # Let the harness enter its native wait after reading the response.
192
+ await asyncio.sleep(0.2)
193
+ sends.extend(asyncio.create_task(send(i)) for i in range(2))
194
+ # Keep the operation held while both ACP requests traverse the real processes.
195
+ await asyncio.sleep(0.25)
196
+ scenario.record("release_operation")
197
+ scenario.model_release.set()
198
+ scenario.tool_release.set()
199
+ await opening
200
+ receipts = await asyncio.gather(*sends)
201
+ # Delivery receipts no longer wait for completion. Keep the session
202
+ # open until its owned turns finish so the trace includes the replies.
203
+ if interaction._message_drain is not None:
204
+ await interaction._message_drain
205
+ assert all(r["outcome"] == "injected" for r in receipts)
206
+ assert set(scenario.calls[-1]["messages"]) == set(LABELS)
207
+ for label, mode in zip(LABELS, modes, strict=True):
208
+ submitted = next(
209
+ e["index"]
210
+ for e in scenario.events
211
+ if e["event"] == "submitted" and e["message"] == label
212
+ )
213
+ seen = next(
214
+ e["index"]
215
+ for e in scenario.events
216
+ if e["event"] == "model_request" and label in e["messages"]
217
+ )
218
+ assert seen > submitted
219
+ if mode == "steer" and (
220
+ not state.startswith("idle")
221
+ or (state == "idle" and label == LABELS[1])
222
+ ):
223
+ assert not any(
224
+ e["event"] == "turn_yielded"
225
+ for e in scenario.events[submitted:seen]
226
+ )
227
+ if (
228
+ mode == "queue"
229
+ and state != "idle-burst"
230
+ and not (state == "idle" and label == LABELS[0])
231
+ ):
232
+ assert any(
233
+ e["event"] == "turn_yielded"
234
+ for e in scenario.events[submitted:seen]
235
+ )
236
+ if (
237
+ kind == "rlm"
238
+ and not state.startswith("idle")
239
+ and modes == ("steer", "steer")
240
+ ):
241
+ assert scenario.calls[1]["messages"] == list(LABELS)
242
+ if not state.startswith("idle") and modes == ("queue", "queue"):
243
+ assert next(c for c in scenario.calls if c["messages"])[
244
+ "messages"
245
+ ] == list(LABELS)
246
+ interaction.trace.info.update(
247
+ scenario=name,
248
+ scripted_inference=True,
249
+ executables=bins,
250
+ events=scenario.events,
251
+ model_inputs=scenario.calls,
252
+ )
253
+ assert interaction.trace.ok, interaction.trace.errors
254
+ finally:
255
+ scenario.model_release.set()
256
+ scenario.tool_release.set()
257
+ for pending in [opening, *sends]:
258
+ if pending is not None and not pending.done():
259
+ pending.cancel()
260
+ await asyncio.gather(
261
+ *(p for p in [opening, *sends] if p is not None), return_exceptions=True
262
+ )
263
+ server.should_exit = True
264
+ await serving
265
+ output = Path(os.environ.get("VF_DELIVERY_OUTPUT", tmp_path)) / name
266
+ output.mkdir(parents=True, exist_ok=True)
267
+ (output / "timeline.json").write_text(
268
+ json.dumps(
269
+ {
270
+ "scenario": name,
271
+ "events": scenario.events,
272
+ "model_inputs": scenario.calls,
273
+ },
274
+ indent=2,
275
+ )
276
+ )
277
+ if "interaction" in locals():
278
+ trace = interaction.trace
279
+ episode = Episode(
280
+ task=trace.task,
281
+ traces=[trace],
282
+ ok=trace.ok,
283
+ run=EvalRunInfo(id=name, name=name),
284
+ )
285
+ (output / "traces.jsonl").write_text(episode.model_dump_json() + "\n")
286
+ print(
287
+ "\n" + name + ": " + " → ".join(str(c["messages"]) for c in scenario.calls)
288
+ )
@@ -66,6 +66,8 @@ from verifiers.v1.harness import Harness, HarnessSession
66
66
  from verifiers.v1.judge import Judge, JudgeResponse, JudgeView
67
67
  from verifiers.v1.judges import (
68
68
  Criterion,
69
+ DecisionJudge,
70
+ DecisionJudgeConfig,
69
71
  ReferenceJudge,
70
72
  ReferenceJudgeConfig,
71
73
  RubricJudge,
@@ -358,6 +360,8 @@ __all__ = [ # noqa: RUF022 - grouped by public API area
358
360
  "Judges",
359
361
  "JudgeResponse",
360
362
  "JudgeView",
363
+ "DecisionJudge",
364
+ "DecisionJudgeConfig",
361
365
  "ReferenceJudge",
362
366
  "ReferenceJudgeConfig",
363
367
  "RubricJudge",
@@ -231,6 +231,10 @@ class ACPHarnessSession(HarnessSession):
231
231
  self._stderr_tail = bytearray()
232
232
  self._stderr_task: asyncio.Task[None] | None = None
233
233
  self._lock = asyncio.Lock()
234
+ self._write_lock = asyncio.Lock()
235
+ self._responses: dict[int, asyncio.Future] = {}
236
+ self._request_id = 0
237
+ self._response_task: asyncio.Task | None = None
234
238
 
235
239
  async def _start(self) -> None:
236
240
  self._stderr_tail.clear()
@@ -242,8 +246,57 @@ class ACPHarnessSession(HarnessSession):
242
246
  process = await self.runtime.open_process(program, self.config.env)
243
247
  self._process = process
244
248
  self._reader = _PacketReader(process.stdout)
249
+ self._response_task = asyncio.create_task(self._read_responses(self._reader))
245
250
  self._stderr_task = asyncio.create_task(self._drain_stderr(process.stderr))
246
251
 
252
+ async def _read_responses(self, reader: _PacketReader) -> None:
253
+ try:
254
+ while True:
255
+ response = await reader.read()
256
+ future = self._responses.get(response.get("id"))
257
+ if future is not None and not future.done():
258
+ future.set_result(response)
259
+ except BaseException as error: # noqa: BLE001 - settle callers on disconnect or shutdown
260
+ for future in self._responses.values():
261
+ if not future.done():
262
+ future.set_exception(
263
+ RuntimeError(f"ACP connection closed: {error}")
264
+ )
265
+
266
+ async def _request(self, operation: str, **payload: Any) -> JsonObject:
267
+ process = self._process
268
+ if process is None or self._response_task is None or self._response_task.done():
269
+ raise RuntimeError("ACP process is not running")
270
+ self._request_id += 1
271
+ request_id = self._request_id
272
+ future = asyncio.get_running_loop().create_future()
273
+ self._responses[request_id] = future
274
+ try:
275
+ async with self._write_lock:
276
+ await process.write(
277
+ _packet({"id": request_id, "operation": operation, **payload})
278
+ )
279
+ return await future
280
+ finally:
281
+ self._responses.pop(request_id, None)
282
+ if not future.done():
283
+ future.cancel()
284
+
285
+ async def steer(self, message: str, *, message_id: str | None = None) -> dict:
286
+ if self._closed:
287
+ raise RuntimeError("ACP session is closed")
288
+ if not isinstance(message, str) or not message.strip():
289
+ raise ValueError("steering requires a nonempty user message")
290
+ if self._process is None:
291
+ return {"outcome": "promptRequired", "reason": "noRunningTurn"}
292
+ response = await self._request("steer", message=message, message_id=message_id)
293
+ if not response.get("ok"):
294
+ raise RuntimeError(response.get("error") or "ACP steering failed")
295
+ result = response.get("result")
296
+ if not isinstance(result, dict):
297
+ raise TypeError("invalid ACP steering receipt")
298
+ return result
299
+
247
300
  async def _drain_stderr(self, stream: AsyncIterator[bytes]) -> None:
248
301
  async for chunk in stream:
249
302
  self._stderr_tail.extend(chunk)
@@ -295,10 +348,7 @@ class ACPHarnessSession(HarnessSession):
295
348
  assert self._reader is not None
296
349
  calls_before = len(self.trace.calls)
297
350
  try:
298
- await self._process.write(
299
- _packet({"operation": "prompt", "config": config})
300
- )
301
- response = await self._reader.read()
351
+ response = await self._request("prompt", config=config)
302
352
  except BaseException:
303
353
  await run_shielded(self._stop(graceful=False))
304
354
  raise
@@ -323,17 +373,18 @@ class ACPHarnessSession(HarnessSession):
323
373
  return result
324
374
 
325
375
  async def _stop(self, *, graceful: bool) -> dict[str, Any]:
326
- process, self._process = self._process, None
327
- reader, self._reader = self._reader, None
376
+ process = self._process
377
+ response_task = self._response_task
328
378
  stderr_task, self._stderr_task = self._stderr_task, None
329
379
  if process is None:
330
380
  return {}
331
381
  response_metadata: dict[str, Any] = {}
332
382
  try:
333
- if graceful and reader is not None:
383
+ if graceful:
334
384
  with contextlib.suppress(BaseException):
335
- await process.write(_packet({"operation": "shutdown"}))
336
- response = await asyncio.wait_for(reader.read(), timeout=10)
385
+ response = await asyncio.wait_for(
386
+ self._request("shutdown"), timeout=10
387
+ )
337
388
  result = response.get("result")
338
389
  if response.get("ok") and isinstance(result, dict):
339
390
  metadata = result.get("response_metadata")
@@ -353,6 +404,13 @@ class ACPHarnessSession(HarnessSession):
353
404
  except TimeoutError:
354
405
  continue
355
406
  finally:
407
+ self._process = None
408
+ self._reader = None
409
+ self._response_task = None
410
+ if response_task is not None:
411
+ response_task.cancel()
412
+ with contextlib.suppress(BaseException):
413
+ await response_task
356
414
  if stderr_task is not None:
357
415
  if not stderr_task.done():
358
416
  stderr_task.cancel()