verifiers 0.3.2.dev124__tar.gz → 0.3.2.dev125__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 (219) hide show
  1. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/PKG-INFO +1 -1
  2. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/test_e2e.py +9 -2
  3. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/test_graph.py +42 -5
  4. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/test_trace.py +91 -4
  5. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/clients/train.py +5 -0
  6. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/dialects/anthropic.py +27 -12
  7. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/dialects/chat.py +24 -16
  8. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/dialects/responses.py +33 -11
  9. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/graph.py +15 -2
  10. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/nemo_gym/response.py +1 -0
  11. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/trace.py +3 -2
  12. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/types.py +9 -2
  13. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/.gitignore +0 -0
  14. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/LICENSE +0 -0
  15. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/README.md +0 -0
  16. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/pyproject.toml +0 -0
  17. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/conftest.py +0 -0
  18. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/fixtures/counter_tool_v1.py +0 -0
  19. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/fixtures/duet_v1.py +0 -0
  20. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/fixtures/echo_acp_resume_v1.py +0 -0
  21. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/fixtures/echo_agentic_v1.py +0 -0
  22. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/fixtures/echo_tool_v1.py +0 -0
  23. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/fixtures/echo_user_sim_v1.py +0 -0
  24. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/fixtures/echo_v1.py +0 -0
  25. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/fixtures/pyproject.toml +0 -0
  26. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/fixtures/tool_response_image_v1.py +0 -0
  27. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/test_color_codeword_tasks.py +0 -0
  28. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/test_configs.py +0 -0
  29. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/test_envs.py +0 -0
  30. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/test_judges.py +0 -0
  31. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/test_platform_run.py +0 -0
  32. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/test_scoring.py +0 -0
  33. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/test_serve_delta.py +0 -0
  34. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/tests/v1/test_taskset.py +0 -0
  35. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/__init__.py +0 -0
  36. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/__init__.py +0 -0
  37. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/acp/__init__.py +0 -0
  38. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/acp/runner.py +0 -0
  39. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/agent.py +0 -0
  40. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/__init__.py +0 -0
  41. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/dashboard/__init__.py +0 -0
  42. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/dashboard/base.py +0 -0
  43. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/dashboard/eval.py +0 -0
  44. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/dashboard/replay.py +0 -0
  45. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/dashboard/validate.py +0 -0
  46. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/debug.py +0 -0
  47. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/eval/__init__.py +0 -0
  48. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/eval/hint.py +0 -0
  49. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/eval/main.py +0 -0
  50. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/eval/resume.py +0 -0
  51. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/eval/runner.py +0 -0
  52. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/gepa.py +0 -0
  53. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/init.py +0 -0
  54. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/output.py +0 -0
  55. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/replay.py +0 -0
  56. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/resolve.py +0 -0
  57. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/resume.py +0 -0
  58. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/cli/validate.py +0 -0
  59. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/clients/__init__.py +0 -0
  60. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/clients/base.py +0 -0
  61. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/clients/client.py +0 -0
  62. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/clients/eval.py +0 -0
  63. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/__init__.py +0 -0
  64. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/agent.py +0 -0
  65. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/cli/__init__.py +0 -0
  66. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/cli/debug.py +0 -0
  67. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/cli/env.py +0 -0
  68. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/cli/eval.py +0 -0
  69. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/cli/init.py +0 -0
  70. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/cli/replay.py +0 -0
  71. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/cli/validate.py +0 -0
  72. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/client.py +0 -0
  73. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/env.py +0 -0
  74. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/harness.py +0 -0
  75. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/judge.py +0 -0
  76. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/retries.py +0 -0
  77. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/runtime.py +0 -0
  78. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/serve.py +0 -0
  79. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/task.py +0 -0
  80. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/configs/taskset.py +0 -0
  81. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/dialects/__init__.py +0 -0
  82. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/dialects/base.py +0 -0
  83. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/env.py +0 -0
  84. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/__init__.py +0 -0
  85. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/agentic_judge/__init__.py +0 -0
  86. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/agentic_judge/env.py +0 -0
  87. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/best_of_n/__init__.py +0 -0
  88. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/best_of_n/env.py +0 -0
  89. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/isolated_verifier/__init__.py +0 -0
  90. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/isolated_verifier/env.py +0 -0
  91. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/shared_agentic_judge/__init__.py +0 -0
  92. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/single_agent/__init__.py +0 -0
  93. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/single_agent/env.py +0 -0
  94. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/user_sim/__init__.py +0 -0
  95. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/envs/user_sim/env.py +0 -0
  96. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/episode.py +0 -0
  97. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/errors.py +0 -0
  98. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/gepa/__init__.py +0 -0
  99. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/gepa/adapter.py +0 -0
  100. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/gepa/config.py +0 -0
  101. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/gepa/dataset.py +0 -0
  102. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/gepa/reflection.py +0 -0
  103. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/gepa/runner.py +0 -0
  104. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harness.py +0 -0
  105. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/__init__.py +0 -0
  106. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/bash/__init__.py +0 -0
  107. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/bash/harness.py +0 -0
  108. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/browser_use/__init__.py +0 -0
  109. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/browser_use/harness.py +0 -0
  110. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/browser_use/program.py +0 -0
  111. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/claude_code/__init__.py +0 -0
  112. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/claude_code/harness.py +0 -0
  113. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/codex/__init__.py +0 -0
  114. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/codex/harness.py +0 -0
  115. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/hermes_agent/__init__.py +0 -0
  116. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/hermes_agent/harness.py +0 -0
  117. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/hermes_agent/program.py +0 -0
  118. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/kimi_code/__init__.py +0 -0
  119. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/kimi_code/harness.py +0 -0
  120. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/mini_swe_agent/__init__.py +0 -0
  121. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/mini_swe_agent/harness.py +0 -0
  122. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/mini_swe_agent/program.py +0 -0
  123. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/node.py +0 -0
  124. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/null/__init__.py +0 -0
  125. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/null/harness.py +0 -0
  126. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/openclaw/__init__.py +0 -0
  127. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/openclaw/harness.py +0 -0
  128. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/pi/__init__.py +0 -0
  129. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/pi/harness.py +0 -0
  130. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/prime_agent/__init__.py +0 -0
  131. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/prime_agent/harness.py +0 -0
  132. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/rlm/__init__.py +0 -0
  133. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/rlm/harness.py +0 -0
  134. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/terminus_2/__init__.py +0 -0
  135. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/terminus_2/harness.py +0 -0
  136. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/terminus_2/program.py +0 -0
  137. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/utils/__init__.py +0 -0
  138. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/utils/compaction.py +0 -0
  139. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/utils/core.py +0 -0
  140. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/utils/install.py +0 -0
  141. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/utils/launch.py +0 -0
  142. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/harnesses/utils/mcp.py +0 -0
  143. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/interception/__init__.py +0 -0
  144. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/interception/base.py +0 -0
  145. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/interception/pool.py +0 -0
  146. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/interception/server.py +0 -0
  147. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/interception/tool.py +0 -0
  148. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/interception/tunnel/__init__.py +0 -0
  149. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/interception/tunnel/base.py +0 -0
  150. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/interception/tunnel/custom.py +0 -0
  151. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/interception/tunnel/prime.py +0 -0
  152. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/judge.py +0 -0
  153. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/judges/__init__.py +0 -0
  154. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/judges/reference.py +0 -0
  155. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/judges/reference.txt +0 -0
  156. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/judges/rubric.py +0 -0
  157. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/judges/rubric.txt +0 -0
  158. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/mcp/__init__.py +0 -0
  159. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/mcp/launch.py +0 -0
  160. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/mcp/server.py +0 -0
  161. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/mcp/toolset.py +0 -0
  162. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/rollout.py +0 -0
  163. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/runtimes/__init__.py +0 -0
  164. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/runtimes/apptainer.py +0 -0
  165. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/runtimes/base.py +0 -0
  166. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/runtimes/container.py +0 -0
  167. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/runtimes/docker/__init__.py +0 -0
  168. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/runtimes/docker/egress.py +0 -0
  169. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/runtimes/limiters.py +0 -0
  170. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/runtimes/modal.py +0 -0
  171. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/runtimes/prime.py +0 -0
  172. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/runtimes/subprocess.py +0 -0
  173. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/semantic.py +0 -0
  174. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/serve/__init__.py +0 -0
  175. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/serve/client.py +0 -0
  176. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/serve/delta.py +0 -0
  177. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/serve/encoding.py +0 -0
  178. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/serve/pool.py +0 -0
  179. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/serve/server.py +0 -0
  180. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/serve/types.py +0 -0
  181. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/session.py +0 -0
  182. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/state.py +0 -0
  183. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/task.py +0 -0
  184. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/taskset.py +0 -0
  185. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/__init__.py +0 -0
  186. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/harbor/__init__.py +0 -0
  187. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/harbor/env.py +0 -0
  188. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/harbor/taskset.py +0 -0
  189. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/harbor/toolset.py +0 -0
  190. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/lean/__init__.py +0 -0
  191. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/lean/scoring.py +0 -0
  192. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/lean/taskset.py +0 -0
  193. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/nemo_gym/__init__.py +0 -0
  194. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/nemo_gym/server.py +0 -0
  195. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/nemo_gym/taskset.py +0 -0
  196. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/nemo_gym/toolset.py +0 -0
  197. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/openenv/__init__.py +0 -0
  198. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/openenv/taskset.py +0 -0
  199. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/textarena/__init__.py +0 -0
  200. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/tasksets/textarena/taskset.py +0 -0
  201. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/__init__.py +0 -0
  202. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/aio.py +0 -0
  203. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/artifacts.py +0 -0
  204. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/compile.py +0 -0
  205. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/decorators.py +0 -0
  206. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/format.py +0 -0
  207. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/generic.py +0 -0
  208. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/git.py +0 -0
  209. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/image.py +0 -0
  210. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/interrupt.py +0 -0
  211. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/loaders.py +0 -0
  212. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/logging.py +0 -0
  213. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/memory.py +0 -0
  214. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/paths.py +0 -0
  215. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/platform.py +0 -0
  216. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/prime.py +0 -0
  217. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/retries.py +0 -0
  218. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/verifiers/v1/utils/score.py +0 -0
  219. {verifiers-0.3.2.dev124 → verifiers-0.3.2.dev125}/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.dev124
3
+ Version: 0.3.2.dev125
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
@@ -373,14 +373,21 @@ async def test_acp_resume_with_tool(run_v1, harness, harness_runtime, tmp_path):
373
373
  # Kimi Code is broken upstream: its Responses adapter drops message `phase` on replay.
374
374
  if harness.id != "kimi-code":
375
375
  assert trace.num_branches == 1
376
- # Native MCP tools need not appear in the intercepted model request that
377
- # populates trace.tools; the ACP transcript is the source of truth for use.
378
376
  assert "tool" in segments[1]["roles"]
379
377
  assert segments[1]["tool_outputs"]
380
378
  if harness.id == "pi":
381
379
  recall = next(tool for tool in trace.tools if tool.name == "resume_recall")
382
380
  assert recall.parameters["properties"]["codeword"]["type"] == "string"
383
381
  assert "codeword" in recall.parameters["required"]
382
+ if harness.id == "codex":
383
+ recall = next(tool for tool in trace.tools if tool.name.endswith("recall"))
384
+ assert recall.namespace
385
+ assert "codeword" in recall.parameters["properties"]
386
+ assert any(
387
+ call.namespace == recall.namespace and call.name == recall.name
388
+ for message in trace.assistant_messages
389
+ for call in message.tool_calls or []
390
+ )
384
391
  if harness.id == "rlm":
385
392
  assert "turns_since_last_compaction" in trace.metrics
386
393
  assert all(call.acp is not None for call in trace.calls)
@@ -1,6 +1,7 @@
1
1
  import base64
2
2
 
3
3
  import numpy as np
4
+ import pytest
4
5
 
5
6
  import verifiers.v1 as vf
6
7
  from verifiers.v1 import graph
@@ -203,6 +204,31 @@ def test_tool_call_hash_matches_v0_content_and_arguments_normalization():
203
204
 
204
205
  assert graph.message_hash(left) == graph.message_hash(right)
205
206
 
207
+ # Identical call IDs and short names can occur in different agent branches.
208
+ trace = vf.Trace(
209
+ agent=vf.AgentInfo(config=vf.AgentConfig()),
210
+ task=vf.TraceTask(type="Task", data=vf.TaskData(idx=0, prompt="lookup")),
211
+ )
212
+ user = vf.UserMessage(content="lookup")
213
+ calls = [
214
+ vf.AssistantMessage(
215
+ tool_calls=[
216
+ vf.ToolCall(id="call_0", name="lookup", namespace=ns, arguments="{}")
217
+ ]
218
+ )
219
+ for ns in (None, "alpha", "beta")
220
+ ]
221
+ node_ids = [
222
+ graph.prepare_turn(trace, [user]).commit(_response(message))
223
+ for message in calls
224
+ ]
225
+ for message, node_id in zip(calls, node_ids, strict=True):
226
+ turn = graph.prepare_turn(
227
+ trace,
228
+ [user, message, vf.ToolMessage(content="result", tool_call_id="call_0")],
229
+ )
230
+ assert turn.prefix_node_ids == [0, node_id]
231
+
206
232
 
207
233
  def test_reasoning_content_participates_in_graph_prefix_matching():
208
234
  task = vf.TaskData(idx=0, prompt="use a tool")
@@ -243,7 +269,8 @@ def test_reasoning_content_participates_in_graph_prefix_matching():
243
269
  assert len(tool_call_nodes) == 2
244
270
 
245
271
 
246
- def test_parallel_commits_reconcile_shared_prompt_prefix():
272
+ @pytest.mark.parametrize("same_tools", [False, True])
273
+ def test_parallel_commits_reconcile_shared_prompt_prefix(same_tools):
247
274
  """Two requests prepared from the same graph snapshot share any common prompt prefix that
248
275
  the first response commits while the second is in flight. A later turn must keep following
249
276
  its original child path rather than re-rooting through the sibling and stranding an orphan."""
@@ -257,10 +284,13 @@ def test_parallel_commits_reconcile_shared_prompt_prefix():
257
284
  assistant_a = vf.AssistantMessage(content="A1")
258
285
 
259
286
  # Both model requests leave before either response has committed its prompt.
260
- pending_a = graph.prepare_turn(trace, [system, user_a])
261
- pending_b = graph.prepare_turn(trace, [system, user_b])
287
+ tool_a = vf.Tool(name="echo", namespace="a")
288
+ tool_b = vf.Tool(name="echo", namespace="a" if same_tools else "b")
289
+ pending_a = graph.prepare_turn(trace, [system, user_a], [tool_a])
290
+ pending_b = graph.prepare_turn(trace, [system, user_b], [tool_b])
262
291
  assistant_a_id = pending_a.commit(_response(assistant_a))
263
292
  pending_b.commit(_response(vf.AssistantMessage(content="B1")))
293
+ assert trace.tools == ([tool_a] if same_tools else [tool_a, tool_b])
264
294
 
265
295
  graph.prepare_turn(
266
296
  trace,
@@ -270,16 +300,23 @@ def test_parallel_commits_reconcile_shared_prompt_prefix():
270
300
  assistant_a,
271
301
  vf.ToolMessage(content="tool A", tool_call_id="call_a"),
272
302
  ],
303
+ [tool_a],
273
304
  ).commit(_response(vf.AssistantMessage(content="A2")))
274
305
 
275
306
  roots = [node for node in trace.nodes if node.parent is None]
276
307
  identities = [
277
- (node.parent, graph.message_hash(node.message)) for node in trace.nodes
308
+ (
309
+ node.parent,
310
+ tuple(tool.namespace for tool in node.tools),
311
+ graph.message_hash(node.message),
312
+ )
313
+ for node in trace.nodes
278
314
  ]
279
- assert len(roots) == 1
315
+ assert len(roots) == (1 if same_tools else 2)
280
316
  assert len(identities) == len(set(identities))
281
317
  assert trace.num_branches == 2
282
318
  assert assistant_a_id not in graph.leaves(trace)
319
+ assert [branch.tools for branch in trace.branches] == [[tool_b], [tool_a]]
283
320
 
284
321
 
285
322
  def test_parallel_commit_reconciles_only_token_identical_prefixes():
@@ -11,7 +11,9 @@ import pytest
11
11
  import verifiers.v1 as vf
12
12
  from verifiers.v1.agent import Interaction
13
13
  from verifiers.v1.cli.output import write_episode
14
- from verifiers.v1.graph import MessageNode
14
+ from verifiers.v1.dialects.chat import ChatDialect
15
+ from verifiers.v1.dialects.responses import ResponsesDialect, fold_assistant
16
+ from verifiers.v1.graph import MessageNode, prepare_turn
15
17
  from verifiers.v1.harnesses.rlm.harness import (
16
18
  RLM_SESSION_METADATA_KEY,
17
19
  RLMHarness,
@@ -174,16 +176,71 @@ def test_custom_task_state_round_trip(tmp_path):
174
176
  assert tr.state.artifacts["/artifact.bin"] == b"\xff\x00\xfe"
175
177
 
176
178
 
177
- def test_wire_trace_round_trip():
179
+ @pytest.mark.parametrize("history_type", ["additional_tools", "tool_search_output"])
180
+ def test_wire_trace_round_trip(history_type):
181
+ mcp = {
182
+ "type": "mcp",
183
+ "server_label": "alpha",
184
+ "server_url": "https://example.invalid/mcp",
185
+ "authorization": "trace-test-oauth-token",
186
+ "headers": {"Authorization": "Bearer trace-test-header-token"},
187
+ "Authorization": "trace-test-case-oauth-token",
188
+ "Headers": {"X-Api-Key": "trace-test-case-header-token"},
189
+ }
190
+ function = {
191
+ "type": "function",
192
+ "name": "echo",
193
+ "description": "Echo the supplied text.",
194
+ "parameters": {"type": "object", "properties": {"text": {"type": "string"}}},
195
+ "strict": False,
196
+ }
197
+ stale = function | {"parameters": {"type": "object"}, "strict": True}
198
+ request = ResponsesDialect().parse_request(
199
+ {
200
+ "input": [
201
+ {
202
+ "type": history_type,
203
+ "call_id": "search",
204
+ "tools": [
205
+ stale,
206
+ {"type": "namespace", "name": "mcp__world", "tools": [stale]},
207
+ ],
208
+ }
209
+ ],
210
+ "tools": [
211
+ function,
212
+ {"type": "namespace", "name": "mcp__world", "tools": [function]},
213
+ {"type": "namespace", "name": "mcp__other", "tools": [function]},
214
+ {"type": "custom", "name": "patch", "format": {"type": "text"}},
215
+ {"type": "web_search", "search_context_size": "low"},
216
+ mcp,
217
+ mcp | {"server_label": "beta"},
218
+ ],
219
+ }
220
+ )
221
+ assert request.tools is not None and len(request.tools) == 7
222
+ assert all(tool.parameters == function["parameters"] for tool in request.tools[:3])
223
+ assert all(tool.strict is False for tool in request.tools[:3])
224
+ assistant = fold_assistant(
225
+ [
226
+ {
227
+ "type": "function_call",
228
+ "call_id": "call_0",
229
+ "name": "echo",
230
+ "namespace": "mcp__world",
231
+ "arguments": '{"text":"hello"}',
232
+ }
233
+ ]
234
+ )
178
235
  # Two leaves off one root → 2 branches (a compaction-shaped trace), so the round-trip has to
179
236
  # carry node `parent` links for `num_branches` to survive.
180
237
  tr = vf.Trace[MyTask, vf.State](
181
238
  agent=vf.AgentInfo(config=vf.AgentConfig()),
182
239
  task=vf.TraceTask(type="MyTask", data=MyTask(idx=0, prompt="q", answer="a")),
183
- tools=[vf.Tool(name="echo", description="", parameters={"type": "object"})],
240
+ tools=request.tools,
184
241
  nodes=[
185
242
  MessageNode(parent=None, message=UserMessage(content="q"), sampled=False),
186
- MessageNode(parent=0, message=AssistantMessage(content="a1"), sampled=True),
243
+ MessageNode(parent=0, message=assistant, sampled=True),
187
244
  MessageNode(parent=0, message=AssistantMessage(content="a2"), sampled=True),
188
245
  ],
189
246
  )
@@ -193,9 +250,26 @@ def test_wire_trace_round_trip():
193
250
  tr.info = {"build": "ok"}
194
251
  tr.root_reply = "root answer"
195
252
  tr.stop("done")
253
+ prepare_turn(tr, [], request.tools).commit_prompt()
196
254
 
197
255
  # the dump is plain pydantic — derived values are properties, so they're not serialized
198
256
  data = json.loads(tr.model_dump_json(exclude_none=True))
257
+ assert "trace-test-" not in json.dumps(data)
258
+ assert mcp["authorization"] == "trace-test-oauth-token"
259
+ assert mcp["headers"] == {"Authorization": "Bearer trace-test-header-token"}
260
+ assert mcp["Authorization"] == "trace-test-case-oauth-token"
261
+ assert mcp["Headers"] == {"X-Api-Key": "trace-test-case-header-token"}
262
+ chat_request = ChatDialect().parse_request(
263
+ {"tools": [mcp, mcp | {"server_label": "beta"}]}
264
+ )
265
+ chat_trace = tr.model_copy(update={"tools": []})
266
+ prepare_turn(chat_trace, [], chat_request.tools).commit_prompt()
267
+ assert [tool.name for tool in chat_trace.tools] == ["alpha", "beta"]
268
+ assert "trace-test-" not in chat_trace.model_dump_json()
269
+ assert (
270
+ vf.WireTrace.model_validate_json(chat_trace.model_dump_json()).tools
271
+ == chat_request.tools
272
+ )
199
273
  assert "reward" not in data and "is_truncated" not in data
200
274
  # exclude_none drops None FIELDS, not None dict values — unscored seeds survive
201
275
  assert data["rewards"]["solved"] is None and data["metrics"]["acc"] is None
@@ -214,6 +288,19 @@ def test_wire_trace_round_trip():
214
288
  assert (
215
289
  rt.tools == tr.tools
216
290
  ) # the advertised tools persist (tool-use SFT reads them)
291
+ assert [tool.namespace for tool in rt.tools[:3]] == [
292
+ None,
293
+ "mcp__world",
294
+ "mcp__other",
295
+ ]
296
+ assert all(tool.parameters == function["parameters"] for tool in rt.tools[:3])
297
+ assert all(tool.strict is False for tool in rt.tools[:3])
298
+ assert rt.tools[3].type == "custom"
299
+ assert rt.tools[3].model_extra == {"format": {"type": "text"}}
300
+ assert rt.tools[4].type == "web_search"
301
+ assert rt.tools[4].model_extra == {"search_context_size": "low"}
302
+ assert [tool.name for tool in rt.tools[5:]] == ["alpha", "beta"]
303
+ assert rt.nodes[1].message.tool_calls[0].namespace == "mcp__world"
217
304
  assert rt.task.data.model_extra == {
218
305
  "answer": "a"
219
306
  } # taskset extras preserved on WireTaskData
@@ -38,6 +38,10 @@ T = TypeVar("T")
38
38
 
39
39
 
40
40
  def tool_to_wire(tool: Tool) -> dict:
41
+ if tool.type != "function" or tool.namespace:
42
+ raise NotImplementedError(
43
+ "The renderer client only supports unnamespaced function tools."
44
+ )
41
45
  function: dict = {
42
46
  "name": tool.name,
43
47
  "description": tool.description,
@@ -61,6 +65,7 @@ def serialize_completion(response: Response, model: str) -> dict:
61
65
  "type": c.type,
62
66
  c.type: {
63
67
  "name": c.name,
68
+ **({"namespace": c.namespace} if c.namespace else {}),
64
69
  "input" if c.type == "custom" else "arguments": c.arguments,
65
70
  },
66
71
  }
@@ -52,7 +52,8 @@ STOP_REASONS: dict[str, FinishReason] = {
52
52
  "stop_sequence": "stop",
53
53
  }
54
54
  # Claude may reorder mixed thinking block types between a response and its replay.
55
- THINKING = ("redacted_thinking", "thinking")
55
+ # Native tool events share the final rank, preserving their relative order.
56
+ THINKING_ORDER = {"redacted_thinking": 0, "thinking": 1}
56
57
  # These versioned tool families return calls to the harness; every other typed tool may execute
57
58
  # at the provider. Anchoring the pattern keeps new versions client-side without treating an
58
59
  # arbitrary dated provider tool as safe.
@@ -259,8 +260,10 @@ def parse_messages(body: dict) -> Messages:
259
260
  if isinstance(content, str)
260
261
  else content or []
261
262
  )
262
- state = [block for block in blocks if block["type"] in THINKING]
263
- state.sort(key=lambda block: THINKING.index(block["type"]))
263
+ state = [
264
+ block for block in blocks if block["type"] not in ("text", "tool_use")
265
+ ]
266
+ state.sort(key=lambda block: THINKING_ORDER.get(block["type"], 2))
264
267
  text = "".join(b.get("text", "") for b in blocks if b.get("type") == "text")
265
268
  reasoning = "".join(
266
269
  b.get("thinking", "") for b in blocks if b.get("type") == "thinking"
@@ -269,6 +272,7 @@ def parse_messages(body: dict) -> Messages:
269
272
  ToolCall(
270
273
  id=b.get("id", ""),
271
274
  name=b.get("name", ""),
275
+ namespace=b.get("toolset_name"),
272
276
  arguments=json.dumps(b.get("input") or {}),
273
277
  )
274
278
  for b in blocks
@@ -311,8 +315,9 @@ def response_from_wire(message: AnthropicMessage) -> Response:
311
315
  reasoning: list[str] = []
312
316
  calls: list[ToolCall] = []
313
317
  for block in message.content:
314
- if block.type in THINKING:
315
- state.append(block.model_dump())
318
+ if block.type not in ("text", "tool_use"):
319
+ # SDK-inserted defaults are absent when the native response is replayed.
320
+ state.append(block.model_dump(exclude_unset=True))
316
321
  if block.type == "text":
317
322
  content.append(block.text)
318
323
  elif block.type == "thinking":
@@ -322,10 +327,11 @@ def response_from_wire(message: AnthropicMessage) -> Response:
322
327
  ToolCall(
323
328
  id=block.id,
324
329
  name=block.name,
330
+ namespace=block.toolset_name,
325
331
  arguments=json.dumps(block.input or {}),
326
332
  )
327
333
  )
328
- state.sort(key=lambda block: THINKING.index(block["type"]))
334
+ state.sort(key=lambda block: THINKING_ORDER.get(block["type"], 2))
329
335
  finish = STOP_REASONS.get(message.stop_reason or "")
330
336
  provider_usage = message.usage
331
337
  output_details = provider_usage.model_dump().get("output_tokens_details")
@@ -580,14 +586,23 @@ class AnthropicDialect(Dialect[AnthropicMessage]):
580
586
  }
581
587
 
582
588
  def parse_request(self, body: RawRequest) -> Request:
589
+ native_tools = body.get("tools") or []
590
+ if not isinstance(native_tools, list) or any(
591
+ not isinstance(tool, dict) for tool in native_tools
592
+ ):
593
+ raise ValueError("tools must be an array of objects")
583
594
  tools = [
584
- Tool(
585
- name=t["name"],
586
- description=t.get("description", ""),
587
- parameters=t.get("input_schema", {}),
595
+ Tool.model_validate(
596
+ {k: v for k, v in t.items() if k != "input_schema"}
597
+ | {
598
+ "name": t.get("name") or t.get("mcp_server_name") or t.get("type"),
599
+ "type": "function"
600
+ if t.get("type") in (None, "custom")
601
+ else t["type"],
602
+ "parameters": t.get("input_schema") or {},
603
+ }
588
604
  )
589
- for t in body.get("tools") or []
590
- if "input_schema" in t # skip server tools (web_search etc.)
605
+ for t in native_tools
591
606
  ] or None
592
607
  return Request(messages=parse_messages(body), tools=tools)
593
608
 
@@ -122,6 +122,7 @@ def parse_message(raw: dict) -> Message:
122
122
  id=call["id"],
123
123
  type=kind,
124
124
  name=native["name"],
125
+ namespace=native.get("namespace"),
125
126
  arguments=native["input" if kind == "custom" else "arguments"],
126
127
  )
127
128
  )
@@ -135,21 +136,26 @@ def parse_message(raw: dict) -> Message:
135
136
 
136
137
 
137
138
  def parse_tools(raw: list[dict] | None) -> list[Tool] | None:
138
- # `or None` so a tools array with no function entries (e.g. only `custom`/built-in
139
- # tools) parses to None, not [] — the same contract as the anthropic/responses
140
- # dialects, and what keeps an empty parse from clearing `Trace.tools`.
141
- if not raw:
142
- return None
143
- return [
144
- Tool(
145
- name=t["function"]["name"],
146
- description=t["function"].get("description", ""),
147
- parameters=t["function"].get("parameters", {}),
148
- strict=t["function"].get("strict"),
139
+ tools = []
140
+ for declaration in raw or []:
141
+ kind = declaration.get("type", "function")
142
+ tool = declaration.get(kind, declaration)
143
+ if kind == "mcp":
144
+ tool = {
145
+ key: value
146
+ for key, value in tool.items()
147
+ if key.lower() not in ("authorization", "headers")
148
+ }
149
+ tools.append(
150
+ Tool.model_validate(
151
+ tool
152
+ | {
153
+ "type": kind,
154
+ "name": tool.get("name") or tool.get("server_label") or kind,
155
+ }
156
+ )
149
157
  )
150
- for t in raw
151
- if t.get("type", "function") == "function"
152
- ] or None
158
+ return tools or None
153
159
 
154
160
 
155
161
  # --- vf -> chat wire ----------------------------------------------------------
@@ -184,6 +190,7 @@ def message_to_wire(message: Message) -> dict:
184
190
  "type": call.type,
185
191
  call.type: {
186
192
  "name": call.name,
193
+ **({"namespace": call.namespace} if call.namespace else {}),
187
194
  "input"
188
195
  if call.type == "custom"
189
196
  else "arguments": call.arguments,
@@ -302,8 +309,9 @@ class ChatStreamParser(StreamParser):
302
309
  slot["type"] = kind
303
310
  native = slot.setdefault(kind, {"name": ""})
304
311
  delta_native = tool_call.get(kind) or {}
305
- if delta_native.get("name"):
306
- native["name"] = delta_native["name"]
312
+ for field in ("name", "namespace"):
313
+ if delta_native.get(field):
314
+ native[field] = delta_native[field]
307
315
  input_field = "input" if kind == "custom" else "arguments"
308
316
  self.tool_inputs.setdefault(index, []).append(
309
317
  delta_native.get(input_field) or ""
@@ -303,6 +303,7 @@ def fold_assistant(items: list[dict] | None) -> AssistantMessage:
303
303
  id=item.get("call_id", ""),
304
304
  type="custom" if kind == "custom_tool_call" else "function",
305
305
  name=item.get("name", ""),
306
+ namespace=item.get("namespace"),
306
307
  arguments=item.get("arguments", item.get("input", "")),
307
308
  )
308
309
  )
@@ -612,17 +613,38 @@ class ResponsesDialect(Dialect[OpenAIResponse]):
612
613
  prompt.append(UserMessage(content=parse_content(item.get("content"))))
613
614
  if run:
614
615
  prompt.append(fold_assistant(run))
615
- tools = [
616
- Tool(
617
- name=t["name"],
618
- description=t.get("description") or "",
619
- parameters=t.get("parameters") or {},
620
- strict=t.get("strict"),
621
- )
622
- for t in body.get("tools") or []
623
- if t.get("type") == "function"
624
- ] or None
625
- return Request(messages=prompt, tools=tools)
616
+ tools = []
617
+ declarations = []
618
+ for item in items:
619
+ if item.get("type") in ("additional_tools", "tool_search_output"):
620
+ declarations.extend(item.get("tools") or [])
621
+ # Current declarations take precedence over definitions replayed in history.
622
+ declarations.extend(body.get("tools") or [])
623
+ for group in declarations:
624
+ namespace = group["name"] if group.get("type") == "namespace" else None
625
+ for tool in group["tools"] if namespace else [group]:
626
+ if tool.get("type") == "mcp":
627
+ # Connection credentials belong in the native request, not the trace.
628
+ tool = {
629
+ key: value
630
+ for key, value in tool.items()
631
+ if key.lower() not in ("authorization", "headers")
632
+ }
633
+ tools.append(
634
+ Tool.model_validate(
635
+ tool
636
+ | {
637
+ "name": tool.get("name")
638
+ or tool.get("server_label")
639
+ or tool.get("type"),
640
+ "namespace": namespace,
641
+ "description": tool.get("description") or "",
642
+ "parameters": tool.get("parameters") or {},
643
+ }
644
+ )
645
+ )
646
+ tools = list({(t.namespace, t.name, t.type): t for t in tools}.values())
647
+ return Request(messages=prompt, tools=tools or None)
626
648
 
627
649
  def parse_response(self, response: OpenAIResponse) -> Response:
628
650
  return response_from_wire(response)
@@ -340,6 +340,7 @@ def message_hash(message: Message) -> str:
340
340
  add(tc.type)
341
341
  add(tc.id)
342
342
  add(tc.name)
343
+ add(tc.namespace or "")
343
344
  add(
344
345
  tc.arguments
345
346
  if tc.type == "custom"
@@ -547,7 +548,13 @@ class PendingTurn:
547
548
  def commit(self, response: Response) -> int:
548
549
  """Add this turn to the graph; returns the committed assistant node's id."""
549
550
  assistant_id = _commit_turn(self, response)
550
- self.trace.tools = self.tools
551
+ if self.tools:
552
+ self.trace.tools = list(
553
+ {
554
+ (tool.namespace, tool.name, tool.type): tool
555
+ for tool in [*self.trace.tools, *self.tools]
556
+ }.values()
557
+ )
551
558
  self.trace.clear_preview(self)
552
559
  return assistant_id
553
560
 
@@ -574,7 +581,13 @@ class PendingTurn:
574
581
  )
575
582
  parent = len(self.trace.nodes) - 1
576
583
  index[_node_key(previous, message, self.tools)] = parent
577
- self.trace.tools = self.tools
584
+ if self.tools:
585
+ self.trace.tools = list(
586
+ {
587
+ (tool.namespace, tool.name, tool.type): tool
588
+ for tool in [*self.trace.tools, *self.tools]
589
+ }.values()
590
+ )
578
591
  self.trace.clear_preview(self)
579
592
 
580
593
 
@@ -70,6 +70,7 @@ def trace_to_nemo_response(
70
70
  else "function_call",
71
71
  "call_id": call.id,
72
72
  "name": call.name,
73
+ **({"namespace": call.namespace} if call.namespace else {}),
73
74
  ("input" if call.type == "custom" else "arguments"): call.arguments,
74
75
  }
75
76
  for call in message.tool_calls or []
@@ -408,7 +408,7 @@ class Trace(BaseModel, Generic[DataT, StateT, AgentConfigT]):
408
408
  agent: AgentInfo[AgentConfigT]
409
409
  """The agent (harness x model x runtime) that produced this trace."""
410
410
  tools: list[Tool] = Field(default_factory=list)
411
- """The tools advertised to the agent, automatically recorded from last intercepted turn."""
411
+ """Tools observed across requests; the latest definition wins for each identity."""
412
412
 
413
413
  nodes: list[MessageNode] = Field(default_factory=list)
414
414
  """The message graph, including physical and semantic parent links."""
@@ -709,7 +709,8 @@ class Trace(BaseModel, Generic[DataT, StateT, AgentConfigT]):
709
709
  if message.content:
710
710
  lines.append(message.content)
711
711
  lines.extend(
712
- f"[tool_call {call.name}({call.arguments})]"
712
+ f"[tool_call {call.namespace + '.' if call.namespace else ''}"
713
+ f"{call.name}({call.arguments})]"
713
714
  for call in message.tool_calls or []
714
715
  )
715
716
  else:
@@ -68,6 +68,8 @@ class ToolCall(BaseModel):
68
68
  id: str
69
69
  type: Literal["function", "custom"] = "function"
70
70
  name: str
71
+ namespace: str | None = None
72
+ """Provider namespace qualifying the tool name, when sent separately."""
71
73
  arguments: str
72
74
  """Raw function arguments or custom-tool input, exactly as the model emitted it."""
73
75
 
@@ -97,9 +99,14 @@ Messages = list[Message]
97
99
 
98
100
 
99
101
  class Tool(BaseModel):
102
+ # Native declarations carry format, discovery, and provider-specific settings.
103
+ model_config = ConfigDict(extra="allow")
104
+
105
+ type: str = "function"
100
106
  name: str
101
- description: str
102
- parameters: dict[str, Any]
107
+ namespace: str | None = None
108
+ description: str = ""
109
+ parameters: dict[str, Any] = Field(default_factory=dict)
103
110
  strict: bool | None = None
104
111
 
105
112