verifiers 0.3.2.dev125__tar.gz → 0.3.2.dev127__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 (223) hide show
  1. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/PKG-INFO +1 -1
  2. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/acp/__init__.py +15 -0
  3. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/acp/runner.py +68 -4
  4. verifiers-0.3.2.dev127/verifiers/v1/configs/runtime.py +126 -0
  5. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/anthropic.py +72 -145
  6. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/base.py +121 -69
  7. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/chat.py +49 -54
  8. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/responses.py +80 -163
  9. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/graph.py +11 -0
  10. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harness.py +4 -0
  11. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/claude_code/harness.py +14 -0
  12. verifiers-0.3.2.dev127/verifiers/v1/harnesses/codex/gate.mjs +34 -0
  13. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/codex/harness.py +46 -0
  14. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/kimi_code/harness.py +14 -0
  15. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/null/harness.py +5 -0
  16. verifiers-0.3.2.dev127/verifiers/v1/harnesses/pi/gate.mjs +11 -0
  17. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/pi/harness.py +18 -0
  18. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/core.py +20 -28
  19. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/server.py +30 -12
  20. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/rollout.py +1 -0
  21. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/modal.py +3 -6
  22. verifiers-0.3.2.dev127/verifiers/v1/session.py +605 -0
  23. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/task.py +2 -2
  24. verifiers-0.3.2.dev125/verifiers/v1/configs/runtime.py +0 -72
  25. verifiers-0.3.2.dev125/verifiers/v1/interception/tool.py +0 -12
  26. verifiers-0.3.2.dev125/verifiers/v1/session.py +0 -418
  27. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/.gitignore +0 -0
  28. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/LICENSE +0 -0
  29. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/README.md +0 -0
  30. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/pyproject.toml +0 -0
  31. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/conftest.py +0 -0
  32. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/counter_tool_v1.py +0 -0
  33. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/duet_v1.py +0 -0
  34. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_acp_resume_v1.py +0 -0
  35. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_agentic_v1.py +0 -0
  36. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_tool_v1.py +0 -0
  37. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_user_sim_v1.py +0 -0
  38. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/echo_v1.py +0 -0
  39. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/pyproject.toml +0 -0
  40. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/fixtures/tool_response_image_v1.py +0 -0
  41. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_color_codeword_tasks.py +0 -0
  42. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_configs.py +0 -0
  43. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_e2e.py +0 -0
  44. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_envs.py +0 -0
  45. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_graph.py +0 -0
  46. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_judges.py +0 -0
  47. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_platform_run.py +0 -0
  48. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_scoring.py +0 -0
  49. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_serve_delta.py +0 -0
  50. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_taskset.py +0 -0
  51. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/tests/v1/test_trace.py +0 -0
  52. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/__init__.py +0 -0
  53. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/__init__.py +0 -0
  54. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/agent.py +0 -0
  55. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/__init__.py +0 -0
  56. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/__init__.py +0 -0
  57. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/base.py +0 -0
  58. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/eval.py +0 -0
  59. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/replay.py +0 -0
  60. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/dashboard/validate.py +0 -0
  61. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/debug.py +0 -0
  62. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/__init__.py +0 -0
  63. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/hint.py +0 -0
  64. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/main.py +0 -0
  65. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/resume.py +0 -0
  66. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/eval/runner.py +0 -0
  67. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/gepa.py +0 -0
  68. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/init.py +0 -0
  69. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/output.py +0 -0
  70. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/replay.py +0 -0
  71. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/resolve.py +0 -0
  72. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/resume.py +0 -0
  73. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/cli/validate.py +0 -0
  74. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/clients/__init__.py +0 -0
  75. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/clients/base.py +0 -0
  76. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/clients/client.py +0 -0
  77. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/clients/eval.py +0 -0
  78. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/clients/train.py +0 -0
  79. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/__init__.py +0 -0
  80. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/agent.py +0 -0
  81. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/__init__.py +0 -0
  82. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/debug.py +0 -0
  83. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/env.py +0 -0
  84. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/eval.py +0 -0
  85. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/init.py +0 -0
  86. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/replay.py +0 -0
  87. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/cli/validate.py +0 -0
  88. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/client.py +0 -0
  89. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/env.py +0 -0
  90. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/harness.py +0 -0
  91. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/judge.py +0 -0
  92. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/retries.py +0 -0
  93. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/serve.py +0 -0
  94. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/task.py +0 -0
  95. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/configs/taskset.py +0 -0
  96. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/dialects/__init__.py +0 -0
  97. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/env.py +0 -0
  98. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/__init__.py +0 -0
  99. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
  100. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/agentic_judge/env.py +0 -0
  101. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
  102. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/best_of_n/env.py +0 -0
  103. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/isolated_verifier/__init__.py +0 -0
  104. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/isolated_verifier/env.py +0 -0
  105. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/shared_agentic_judge/__init__.py +0 -0
  106. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/single_agent/__init__.py +0 -0
  107. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/single_agent/env.py +0 -0
  108. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/user_sim/__init__.py +0 -0
  109. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/envs/user_sim/env.py +0 -0
  110. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/episode.py +0 -0
  111. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/errors.py +0 -0
  112. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/__init__.py +0 -0
  113. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/adapter.py +0 -0
  114. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/config.py +0 -0
  115. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/dataset.py +0 -0
  116. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/reflection.py +0 -0
  117. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/gepa/runner.py +0 -0
  118. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/__init__.py +0 -0
  119. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/bash/__init__.py +0 -0
  120. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/bash/harness.py +0 -0
  121. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
  122. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
  123. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/browser_use/program.py +0 -0
  124. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
  125. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/codex/__init__.py +0 -0
  126. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
  127. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
  128. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
  129. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
  130. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
  131. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
  132. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
  133. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/node.py +0 -0
  134. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/null/__init__.py +0 -0
  135. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
  136. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
  137. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/pi/__init__.py +0 -0
  138. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
  139. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
  140. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
  141. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/rlm/harness.py +0 -0
  142. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
  143. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
  144. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
  145. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/__init__.py +0 -0
  146. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/compaction.py +0 -0
  147. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/install.py +0 -0
  148. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/launch.py +0 -0
  149. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/harnesses/utils/mcp.py +0 -0
  150. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/__init__.py +0 -0
  151. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/base.py +0 -0
  152. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/pool.py +0 -0
  153. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/__init__.py +0 -0
  154. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/base.py +0 -0
  155. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/custom.py +0 -0
  156. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/interception/tunnel/prime.py +0 -0
  157. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judge.py +0 -0
  158. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judges/__init__.py +0 -0
  159. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judges/reference.py +0 -0
  160. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judges/reference.txt +0 -0
  161. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judges/rubric.py +0 -0
  162. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/judges/rubric.txt +0 -0
  163. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/__init__.py +0 -0
  164. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/launch.py +0 -0
  165. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/server.py +0 -0
  166. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/mcp/toolset.py +0 -0
  167. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/__init__.py +0 -0
  168. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/apptainer.py +0 -0
  169. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/base.py +0 -0
  170. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/container.py +0 -0
  171. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/docker/__init__.py +0 -0
  172. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/docker/egress.py +0 -0
  173. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/limiters.py +0 -0
  174. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/prime.py +0 -0
  175. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/runtimes/subprocess.py +0 -0
  176. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/semantic.py +0 -0
  177. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/__init__.py +0 -0
  178. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/client.py +0 -0
  179. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/delta.py +0 -0
  180. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/encoding.py +0 -0
  181. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/pool.py +0 -0
  182. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/server.py +0 -0
  183. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/serve/types.py +0 -0
  184. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/state.py +0 -0
  185. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/taskset.py +0 -0
  186. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/__init__.py +0 -0
  187. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
  188. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/env.py +0 -0
  189. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
  190. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
  191. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/lean/__init__.py +0 -0
  192. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/lean/scoring.py +0 -0
  193. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/lean/taskset.py +0 -0
  194. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
  195. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/response.py +0 -0
  196. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
  197. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
  198. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
  199. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
  200. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
  201. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
  202. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
  203. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/trace.py +0 -0
  204. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/types.py +0 -0
  205. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/__init__.py +0 -0
  206. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/aio.py +0 -0
  207. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/artifacts.py +0 -0
  208. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/compile.py +0 -0
  209. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/decorators.py +0 -0
  210. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/format.py +0 -0
  211. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/generic.py +0 -0
  212. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/git.py +0 -0
  213. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/image.py +0 -0
  214. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/interrupt.py +0 -0
  215. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/loaders.py +0 -0
  216. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/logging.py +0 -0
  217. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/memory.py +0 -0
  218. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/paths.py +0 -0
  219. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/platform.py +0 -0
  220. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/prime.py +0 -0
  221. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/retries.py +0 -0
  222. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/verifiers/v1/utils/score.py +0 -0
  223. {verifiers-0.3.2.dev125 → verifiers-0.3.2.dev127}/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.dev125
3
+ Version: 0.3.2.dev127
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
@@ -85,6 +85,13 @@ class ACPHarness(Harness[ConfigT]):
85
85
  )
86
86
  trace.add_semantic_edges(edge_set)
87
87
 
88
+ async def gate_tools(
89
+ self, config: ACPConfig, runtime: Runtime, url: str, secret: str
90
+ ) -> None:
91
+ """Configure the agent to ask before every tool call, so each request reaches the
92
+ runner's gate (`/tool` at `url`, keyed by `secret`). A harness that advertises
93
+ `SUPPORTS_TOOL_INTERCEPTION` implements this with the agent's own config."""
94
+
88
95
  @abstractmethod
89
96
  async def prepare_acp(
90
97
  self,
@@ -116,6 +123,8 @@ class ACPHarness(Harness[ConfigT]):
116
123
  config = await self.prepare_acp(
117
124
  ctx, trace, runtime, endpoint, secret, mcp_urls, data
118
125
  )
126
+ if tool_interception_url is not None:
127
+ await self.gate_tools(config, runtime, tool_interception_url, secret)
119
128
  return ACPHarnessSession(
120
129
  self,
121
130
  ctx,
@@ -268,6 +277,12 @@ class ACPHarnessSession(HarnessSession):
268
277
  "system_prompt": self.config.system_prompt or "",
269
278
  "session_meta": self.config.session_meta or {},
270
279
  "client_capabilities": self.config.client_capabilities or {},
280
+ "tool_interception": {
281
+ "url": self.tool_interception_url,
282
+ "secret": self.secret,
283
+ }
284
+ if self.tool_interception_url
285
+ else None,
271
286
  }
272
287
  async with self._lock:
273
288
  if self._closed:
@@ -1,6 +1,6 @@
1
1
  # /// script
2
2
  # requires-python = ">=3.10,<3.15"
3
- # dependencies = ["agent-client-protocol==0.12.1"]
3
+ # dependencies = ["agent-client-protocol==0.12.1", "httpx"]
4
4
  # ///
5
5
  """Run harness segments through an ACP agent."""
6
6
 
@@ -14,6 +14,7 @@ from contextlib import AsyncExitStack, suppress
14
14
  from dataclasses import asdict, dataclass
15
15
  from typing import Any
16
16
 
17
+ import httpx
17
18
  from acp import (
18
19
  PROTOCOL_VERSION,
19
20
  Client,
@@ -45,8 +46,42 @@ class ACPTurn:
45
46
  update_metadata: list[dict[str, Any]]
46
47
 
47
48
 
49
+ class ToolGate:
50
+ """The rollout's `/tool` gate, asked before every tool call the agent wants to run."""
51
+
52
+ def __init__(self, url: str, secret: str) -> None:
53
+ self.url = url
54
+ self.client = httpx.AsyncClient(
55
+ headers={"Authorization": f"Bearer {secret}"},
56
+ timeout=httpx.Timeout(120, connect=5),
57
+ )
58
+
59
+ async def decision(self, tool_call_id: str, arguments: Any) -> str:
60
+ try:
61
+ # pi-acp wraps extension confirmations in a separate UI permission call.
62
+ if (
63
+ tool_call_id.startswith("pi-ui-")
64
+ and isinstance(arguments, dict)
65
+ and arguments.get("method") == "confirm"
66
+ ):
67
+ tool_call_id, arguments = (
68
+ arguments["title"],
69
+ json.loads(arguments["message"]),
70
+ )
71
+ response = await self.client.post(
72
+ self.url, json={"tool_call_id": tool_call_id, "arguments": arguments}
73
+ )
74
+ response.raise_for_status()
75
+ return response.json()["action"]
76
+ except Exception as error: # noqa: BLE001 - an unreachable gate lets nothing run
77
+ print(f"tool gate denied {tool_call_id}: {error}", file=sys.stderr)
78
+ return "deny"
79
+
80
+
48
81
  class VerifiersACPClient(Client):
49
82
  def __init__(self) -> None:
83
+ self.gate: ToolGate | None = None
84
+ self.prompt_task: asyncio.Task | None = None
50
85
  self.visible_reply = ""
51
86
  self.message_id: str | None = None
52
87
  self.stop_reason: str | None = None
@@ -90,9 +125,23 @@ class VerifiersACPClient(Client):
90
125
  options: list[PermissionOption],
91
126
  **kwargs: Any,
92
127
  ) -> RequestPermissionResponse:
128
+ """Ask the rollout before execution: deny rejects one call; stop cancels the turn."""
129
+ kinds = ("allow_once", "allow_always")
130
+ if self.gate is not None:
131
+ decision = await self.gate.decision(
132
+ tool_call.tool_call_id, tool_call.raw_input
133
+ )
134
+ if decision == "stop":
135
+ self.stop_reason = "cancelled"
136
+ if self.prompt_task is not None:
137
+ self.prompt_task.cancel()
138
+ return RequestPermissionResponse(
139
+ outcome=DeniedOutcome(outcome="cancelled")
140
+ )
141
+ if decision != "allow":
142
+ kinds = ("reject_once", "reject_always")
93
143
  option = next(
94
- (item for item in options if item.kind in ("allow_once", "allow_always")),
95
- None,
144
+ (item for kind in kinds for item in options if item.kind == kind), None
96
145
  )
97
146
  outcome = (
98
147
  AllowedOutcome(outcome="selected", option_id=option.option_id)
@@ -165,12 +214,21 @@ async def prompt(
165
214
  if not blocks:
166
215
  raise ValueError("ACP prompt has no content")
167
216
  try:
168
- response = await connection.prompt(session_id=session_id, prompt=blocks)
217
+ client.prompt_task = asyncio.create_task(
218
+ connection.prompt(session_id=session_id, prompt=blocks)
219
+ )
220
+ response = await client.prompt_task
169
221
  client.stop_reason = response.stop_reason
170
222
  client.response_metadata = dict(response.field_meta or {})
223
+ except asyncio.CancelledError:
224
+ if client.stop_reason != "cancelled":
225
+ raise
226
+ await connection.cancel(session_id=session_id)
171
227
  except RequestError as error:
172
228
  detail = error.data.get("details") if isinstance(error.data, dict) else None
173
229
  raise RuntimeError(detail or str(error)) from error
230
+ finally:
231
+ client.prompt_task = None
174
232
  return client.turn_result()
175
233
 
176
234
 
@@ -222,6 +280,9 @@ class ACPSession:
222
280
  self.is_new = True
223
281
 
224
282
  async def run(self, config: dict) -> ACPTurn:
283
+ gate = config.get("tool_interception")
284
+ if gate and self.client.gate is None:
285
+ self.client.gate = ToolGate(gate["url"], gate["secret"])
225
286
  if self.connection is None:
226
287
  await self.start(config)
227
288
  assert self.session_id is not None
@@ -253,6 +314,9 @@ class ACPSession:
253
314
  try:
254
315
  await self.stack.aclose()
255
316
  finally:
317
+ if self.client.gate is not None:
318
+ await self.client.gate.client.aclose()
319
+ self.client.gate = None
256
320
  self._reset()
257
321
  return response_metadata
258
322
 
@@ -0,0 +1,126 @@
1
+ """Shared configuration for execution-time network policy."""
2
+
3
+ from fnmatch import fnmatchcase
4
+ from glob import has_magic
5
+ from itertools import product
6
+ from typing import Self
7
+ from urllib.parse import SplitResult, urlsplit
8
+
9
+ from pydantic import Field, model_validator
10
+ from pydantic_config import BaseConfig
11
+
12
+
13
+ def parse_network_rule(rule: str) -> tuple[SplitResult, str, int | None]:
14
+ """Normalize host/port while retaining URL syntax for backend validation."""
15
+ parsed = urlsplit(rule if "://" in rule else f"//{rule}")
16
+ port = parsed.port
17
+ if parsed.scheme and port is None:
18
+ port = 443 if parsed.scheme == "https" else 80
19
+ return parsed, (parsed.hostname or "").lower().rstrip("."), port
20
+
21
+
22
+ def network_rule_matches(rule: str, scheme: str, host: str, port: int) -> bool:
23
+ """Match a network-policy host pattern or URL origin. Paths are ignored."""
24
+ try:
25
+ parsed, pattern, rule_port = parse_network_rule(rule)
26
+ except ValueError:
27
+ return False
28
+ if not pattern or (parsed.scheme and parsed.scheme != scheme):
29
+ return False
30
+ if rule_port is not None and rule_port != port:
31
+ return False
32
+ host = host.lower().rstrip(".")
33
+ return fnmatchcase(host, pattern) or (
34
+ pattern.startswith("*.") and host == pattern[2:]
35
+ )
36
+
37
+
38
+ def intersect_network_hosts(left: str, right: str) -> str:
39
+ """Return the narrower host pattern, rejecting unprovable glob overlaps."""
40
+ for parent, child in ((left, right), (right, left)):
41
+ if parent in ("*", child):
42
+ return child
43
+ # Matching a pattern as text proves containment only for domain suffixes.
44
+ if has_magic(child) and not (
45
+ parent.startswith("*.") and not has_magic(parent[2:])
46
+ ):
47
+ continue
48
+ if fnmatchcase(child, parent) or child == parent.removeprefix("*."):
49
+ return child
50
+ if all(has_magic(host) for host in (left, right)) and any(
51
+ has_magic(host.removeprefix("*.")) for host in (left, right)
52
+ ):
53
+ raise ValueError(f"cannot intersect network hosts {left!r} and {right!r}")
54
+ return ""
55
+
56
+
57
+ class NetworkPolicyConfig(BaseConfig):
58
+ """Shared execution-time policy surface for runtimes that support it."""
59
+
60
+ allow: list[str] = Field(default_factory=lambda: ["*"])
61
+ """Destinations allowed during execution; `*` is unrestricted and `[]` is
62
+ framework-only."""
63
+ block: list[str] = Field(default_factory=list)
64
+ """Destinations denied during execution; any `*` makes the policy framework-only."""
65
+
66
+ @model_validator(mode="after")
67
+ def validate_network_policy(self) -> Self:
68
+ if not self.allow or "*" in self.block:
69
+ # Empty allowlists and wildcard blocks both mean framework-only access.
70
+ self.allow = []
71
+ self.block = ["*"]
72
+ elif self.allow != ["*"] and self.block:
73
+ raise ValueError(
74
+ "non-empty concrete allow and block egress lists are mutually exclusive"
75
+ )
76
+ return self
77
+
78
+ @property
79
+ def network_restricted(self) -> bool:
80
+ return "*" not in self.allow or bool(self.block)
81
+
82
+ def permits(self, scheme: str, host: str, port: int) -> bool:
83
+ """Whether the destination is allowed by the configured egress rules."""
84
+ return not any(
85
+ network_rule_matches(rule, scheme, host, port) for rule in self.block
86
+ ) and any(network_rule_matches(rule, scheme, host, port) for rule in self.allow)
87
+
88
+ def with_task_network_policy(self, allow: list[str], block: list[str]) -> Self:
89
+ values = self.model_dump()
90
+ # Intersection must not erase syntax the runtime cannot enforce.
91
+ task = type(self).model_validate({**values, "allow": allow, "block": block})
92
+ allow, block = task.allow, task.block
93
+ if "*" in allow:
94
+ allow = self.allow
95
+ elif "*" not in self.allow:
96
+ intersection = []
97
+ for left, right in product(allow, self.allow):
98
+ if left == right:
99
+ intersection.append(left)
100
+ continue
101
+ a, a_host, a_port = parse_network_rule(left)
102
+ b, b_host, b_port = parse_network_rule(right)
103
+ # Bare paths can be provider-specific CIDRs, not URL paths to discard.
104
+ if any(
105
+ not url.hostname or (url.path and not url.scheme) for url in (a, b)
106
+ ):
107
+ raise ValueError(
108
+ f"cannot intersect network rules {left!r} and {right!r}"
109
+ )
110
+ if (
111
+ len({a.scheme, b.scheme} - {""}) > 1
112
+ or len({a_port, b_port} - {None}) > 1
113
+ ):
114
+ continue
115
+ host = intersect_network_hosts(a_host, b_host)
116
+ if not host:
117
+ continue
118
+ scheme = a.scheme or b.scheme
119
+ port = a_port if a_port is not None else b_port
120
+ authority = f"[{host}]" if ":" in host else host
121
+ if port is not None:
122
+ authority = f"{authority}:{port}"
123
+ intersection.append(f"{scheme}://{authority}" if scheme else authority)
124
+ allow = list(dict.fromkeys(intersection))
125
+ block = list(dict.fromkeys([*block, *self.block]))
126
+ return type(self).model_validate({**values, "allow": allow, "block": block})
@@ -18,9 +18,9 @@ from verifiers.v1.configs.runtime import NetworkPolicyConfig
18
18
  from verifiers.v1.dialects.base import (
19
19
  Dialect,
20
20
  RawRequest,
21
+ RequestFilter,
21
22
  StreamParser,
22
23
  append_user_notice,
23
- blocked_url,
24
24
  parse_sse_event,
25
25
  provider_domains,
26
26
  )
@@ -129,89 +129,66 @@ def parse_content(content) -> str | list[ContentPart]:
129
129
  return parts
130
130
 
131
131
 
132
- def blocked_content_path(
133
- value, path: str, policy: NetworkPolicyConfig, blocked_urls: list[str]
134
- ) -> str | None:
135
- if isinstance(value, list):
136
- for index, item in enumerate(value):
137
- if blocked := blocked_content_path(
138
- item, f"{path}[{index}]", policy, blocked_urls
139
- ):
140
- return blocked
141
- return None
142
- if not isinstance(value, dict):
143
- return None
132
+ class AnthropicRequestFilter(RequestFilter):
133
+ wrappers = _CONTENT_WRAPPERS
144
134
 
145
- kind = value.get("type")
146
- caller = value.get("caller")
147
- if caller is not None and not (
148
- isinstance(caller, dict) and caller.get("type") == "direct"
149
- ):
150
- return f"{path}.caller.type"
151
- if kind in ("image", "document"):
152
- source_path = f"{path}.source"
153
- source = value.get("source") or {}
154
- if not isinstance(source, dict):
155
- return source_path
156
- source_kind = source.get("type")
157
- if source_kind == "content":
158
- return blocked_content_path(
159
- source.get("content"), f"{source_path}.content", policy, blocked_urls
135
+ def tool(self, tool, path: str) -> dict | None:
136
+ kind = tool.get("type") if isinstance(tool, dict) else None
137
+ if isinstance(kind, str) and _WEB_TOOL_TYPE(kind):
138
+ callers = tool.get("allowed_callers")
139
+ filter_key = "blocked_domains" if self.policy.block else "allowed_domains"
140
+ other_key = "allowed_domains" if self.policy.block else "blocked_domains"
141
+ domains = (
142
+ provider_domains(self.policy, tool.get(filter_key))
143
+ # Anthropic does not support combining allow and block filters.
144
+ if tool.get(other_key) in (None, [])
145
+ and (
146
+ callers is None or isinstance(callers, list) and "direct" in callers
147
+ )
148
+ else []
160
149
  )
161
- if source_kind == "url":
162
- url = source.get("url")
163
- if not isinstance(url, str) or blocked_url(url, policy, blocked_urls):
150
+ if domains:
151
+ web_tool = {**tool, filter_key: domains, "allowed_callers": ["direct"]}
152
+ web_tool.pop(other_key, None)
153
+ return web_tool
154
+ if isinstance(tool, dict) and (
155
+ (isinstance(kind, str) and _CLIENT_TOOL_TYPE(kind))
156
+ or (kind in (None, "custom") and "input_schema" in tool)
157
+ ):
158
+ return tool
159
+ self.capabilities.append(f"{path}.type")
160
+ return None
161
+
162
+ def blocked_part(self, value: dict, path: str) -> str | None:
163
+ kind = value.get("type")
164
+ if kind in ("image", "document"):
165
+ source_path = f"{path}.source"
166
+ source = value.get("source") or {}
167
+ if not isinstance(source, dict):
168
+ return source_path
169
+ source_kind = source.get("type")
170
+ if source_kind == "content":
171
+ return self.blocked(source.get("content"), f"{source_path}.content")
172
+ if source_kind == "url" and self.blocked_url(source.get("url")):
164
173
  return f"{source_path}.url"
165
- if source_kind == "file":
166
- return (
167
- f"{source_path}.file_id"
168
- if source.get("file_id")
169
- else f"{source_path}.type"
170
- )
171
- if source_kind not in ("base64", "text", "url"):
172
- return f"{source_path}.type"
173
-
174
- if kind in (
175
- "container_upload",
176
- "code_execution_output",
177
- "bash_code_execution_output",
178
- ) and value.get("file_id"):
179
- return f"{path}.file_id"
180
- if kind in _CONTENT_WRAPPERS:
181
- return blocked_content_path(
182
- value.get("content"), f"{path}.content", policy, blocked_urls
183
- )
184
- return None if kind in _SAFE_CONTENT_TYPES else f"{path}.type"
185
-
186
-
187
- def mediate_content(
188
- value, path: str, policy: NetworkPolicyConfig, blocked_urls: list[str]
189
- ):
190
- if not isinstance(value, list):
191
- blocked = blocked_content_path(value, path, policy, blocked_urls)
192
- return ("", [blocked]) if blocked else (value, [])
193
-
194
- mediated = []
195
- capabilities = []
196
- for index, block in enumerate(value):
197
- item_path = f"{path}[{index}]"
198
- if isinstance(block, dict) and block.get("type") in _CONTENT_WRAPPERS:
199
- if blocked := blocked_content_path(
200
- {**block, "content": []}, item_path, policy, blocked_urls
201
- ):
202
- capabilities.append(blocked)
203
- continue
204
- content, removed = mediate_content(
205
- block.get("content"), f"{item_path}.content", policy, blocked_urls
206
- )
207
- if removed:
208
- block["content"] = content or ""
209
- capabilities.extend(removed)
210
- elif blocked := blocked_content_path(block, item_path, policy, blocked_urls):
211
- capabilities.append(blocked)
212
- continue
213
- mediated.append(block)
214
- return mediated, capabilities
174
+ if source_kind == "file":
175
+ return (
176
+ f"{source_path}.file_id"
177
+ if source.get("file_id")
178
+ else f"{source_path}.type"
179
+ )
180
+ if source_kind not in ("base64", "text", "url"):
181
+ return f"{source_path}.type"
182
+
183
+ if kind in (
184
+ "container_upload",
185
+ "code_execution_output",
186
+ "bash_code_execution_output",
187
+ ) and value.get("file_id"):
188
+ return f"{path}.file_id"
189
+ if kind in self.wrappers:
190
+ return self.blocked(value.get("content"), f"{path}.content")
191
+ return None if kind in _SAFE_CONTENT_TYPES else f"{path}.type"
215
192
 
216
193
 
217
194
  def content_to_wire(content) -> str | list[dict]:
@@ -464,80 +441,29 @@ class AnthropicDialect(Dialect[AnthropicMessage]):
464
441
  self, body: RawRequest, policy: NetworkPolicyConfig
465
442
  ) -> tuple[RawRequest, list[str]]:
466
443
  mediated = body
467
- capabilities: list[str] = []
468
- blocked_urls: list[str] = []
444
+ request_filter = AnthropicRequestFilter(policy)
445
+ capabilities = request_filter.capabilities
469
446
 
470
447
  for key in ("container", "mcp_servers"):
471
448
  if mediated.pop(key, None):
472
449
  capabilities.append(key)
473
450
 
474
- system, removed = mediate_content(
475
- mediated.get("system"), "system", policy, blocked_urls
476
- )
477
- capabilities.extend(removed)
478
- if removed:
479
- if system:
480
- mediated["system"] = system
481
- else:
482
- mediated.pop("system")
451
+ if (
452
+ request_filter.content(mediated, "system", "system")
453
+ and not mediated["system"]
454
+ ):
455
+ mediated.pop("system")
483
456
 
484
457
  for message_index, message in enumerate(mediated.get("messages") or []):
485
458
  if not isinstance(message, dict):
486
459
  continue
487
- content, removed = mediate_content(
488
- message.get("content"),
460
+ request_filter.content(
461
+ message,
462
+ "content",
489
463
  f"messages[{message_index}].content",
490
- policy,
491
- blocked_urls,
492
464
  )
493
- capabilities.extend(removed)
494
- if removed:
495
- message["content"] = content or ""
496
-
497
- raw_tools = mediated.get("tools")
498
- tool_items = raw_tools if isinstance(raw_tools, list) else []
499
- if raw_tools is not None and not isinstance(raw_tools, list):
500
- capabilities.append("tools")
501
- tools = []
502
- for index, tool in enumerate(tool_items):
503
- kind = tool.get("type") if isinstance(tool, dict) else None
504
- if (
505
- isinstance(tool, dict)
506
- and isinstance(kind, str)
507
- and _WEB_TOOL_TYPE(kind)
508
- ):
509
- callers = tool.get("allowed_callers")
510
- filter_key = "blocked_domains" if policy.block else "allowed_domains"
511
- other_key = "allowed_domains" if policy.block else "blocked_domains"
512
- domains = (
513
- provider_domains(policy, tool.get(filter_key))
514
- # Anthropic does not support combining allow and block filters.
515
- if tool.get(other_key) in (None, [])
516
- and (
517
- callers is None
518
- or isinstance(callers, list)
519
- and "direct" in callers
520
- )
521
- else []
522
- )
523
- if domains:
524
- web_tool = {
525
- **tool,
526
- filter_key: domains,
527
- "allowed_callers": ["direct"],
528
- }
529
- web_tool.pop(other_key, None)
530
- tools.append(web_tool)
531
- continue
532
- capabilities.append(f"tools[{index}].type")
533
- continue
534
- if isinstance(tool, dict) and (
535
- (isinstance(kind, str) and _CLIENT_TOOL_TYPE(kind))
536
- or (kind in (None, "custom") and "input_schema" in tool)
537
- ):
538
- tools.append(tool)
539
- continue
540
- capabilities.append(f"tools[{index}].type")
465
+
466
+ tools = request_filter.tools(mediated.get("tools"))
541
467
  if "tools" in mediated:
542
468
  mediated["tools"] = tools
543
469
 
@@ -562,7 +488,8 @@ class AnthropicDialect(Dialect[AnthropicMessage]):
562
488
 
563
489
  if capabilities:
564
490
  append_user_notice(
565
- mediated.setdefault("messages", []), blocked_urls=blocked_urls
491
+ mediated.setdefault("messages", []),
492
+ blocked_urls=request_filter.blocked_urls,
566
493
  )
567
494
  return mediated, capabilities
568
495