hud 0.6.18.dev2__tar.gz → 0.6.19.dev2__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 (296) hide show
  1. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/PKG-INFO +13 -15
  2. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/README.md +11 -13
  3. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/__init__.py +2 -0
  4. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/__init__.py +20 -41
  5. hud-0.6.19.dev2/hud/agents/base.py +47 -0
  6. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/browser_use/agent.py +2 -13
  7. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/__init__.py +3 -3
  8. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/agent.py +42 -25
  9. hud-0.6.19.dev2/hud/agents/claude/images.py +65 -0
  10. hud-0.6.19.dev2/hud/agents/claude/sdk/__init__.py +7 -0
  11. hud-0.6.19.dev2/hud/agents/claude/sdk/agent.py +219 -0
  12. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/sdk/computer_mcp.py +50 -108
  13. hud-0.6.19.dev2/hud/agents/claude/sdk/events.py +128 -0
  14. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/tools/coding.py +1 -1
  15. hud-0.6.19.dev2/hud/agents/cli.py +141 -0
  16. hud-0.6.19.dev2/hud/agents/codex/__init__.py +7 -0
  17. hud-0.6.19.dev2/hud/agents/codex/agent.py +336 -0
  18. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/agent.py +4 -2
  19. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/agent.py +4 -2
  20. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai_compatible/agent.py +14 -19
  21. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai_compatible/tools/filesystem.py +4 -3
  22. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/robot/agent.py +0 -1
  23. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/robot/batching.py +1 -0
  24. hud-0.6.19.dev2/hud/agents/tests/cli_fakes.py +111 -0
  25. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tests/test_base.py +37 -14
  26. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tests/test_claude_agent.py +117 -0
  27. hud-0.6.19.dev2/hud/agents/tests/test_claude_cli_agent.py +835 -0
  28. hud-0.6.19.dev2/hud/agents/tests/test_codex_cli_agent.py +381 -0
  29. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tests/test_openai_compatible_agent.py +4 -3
  30. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tests/test_provider_native_tools.py +55 -2
  31. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tests/test_tool_agent.py +38 -19
  32. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tool_agent.py +33 -92
  33. hud-0.6.19.dev2/hud/agents/tools/file_view.py +38 -0
  34. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tools/ssh.py +9 -0
  35. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/types.py +58 -33
  36. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/capabilities/__init__.py +2 -0
  37. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/capabilities/base.py +3 -0
  38. hud-0.6.19.dev2/hud/capabilities/connection.py +106 -0
  39. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/capabilities/ssh.py +44 -8
  40. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/capabilities/tests/test_ssh.py +51 -2
  41. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/eval.py +9 -10
  42. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/test_eval.py +3 -2
  43. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/clients/client.py +42 -7
  44. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/clients/tests/test_connect.py +85 -1
  45. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/__init__.py +2 -1
  46. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/egress.py +241 -1
  47. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/env.py +168 -1
  48. hud-0.6.19.dev2/hud/environment/process_guard.py +723 -0
  49. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/server.py +45 -2
  50. hud-0.6.19.dev2/hud/environment/tests/test_process_connection.py +368 -0
  51. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_sessions.py +73 -1
  52. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_workspace.py +323 -6
  53. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/workspace.py +234 -27
  54. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/run.py +49 -30
  55. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/runtime/__init__.py +2 -1
  56. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/runtime/compose.py +49 -4
  57. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/runtime/docker.py +8 -1
  58. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/runtime/hosted.py +7 -13
  59. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/tests/test_docker_provider.py +88 -0
  60. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/tests/test_hosted.py +52 -20
  61. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/tests/test_local_runtime.py +1 -0
  62. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/tests/test_rollout.py +25 -1
  63. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/tests/test_task.py +8 -4
  64. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/test_integration.py +1 -0
  65. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/telemetry/exporter.py +47 -29
  66. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/telemetry/tests/test_exporter.py +24 -0
  67. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/types.py +40 -19
  68. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/gateway.py +27 -11
  69. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/tests/test_gateway.py +18 -2
  70. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/version.py +1 -1
  71. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/pyproject.toml +1 -1
  72. hud-0.6.18.dev2/hud/agents/base.py +0 -22
  73. hud-0.6.18.dev2/hud/agents/claude/sdk/__init__.py +0 -5
  74. hud-0.6.18.dev2/hud/agents/claude/sdk/agent.py +0 -355
  75. hud-0.6.18.dev2/hud/agents/tests/test_claude_sdk_agent.py +0 -679
  76. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/.gitignore +0 -0
  77. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/LICENSE +0 -0
  78. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/__main__.py +0 -0
  79. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/browser_use/__init__.py +0 -0
  80. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/tools/__init__.py +0 -0
  81. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/tools/base.py +0 -0
  82. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/tools/computer.py +0 -0
  83. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/tools/hosted.py +0 -0
  84. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/tools/mcp_proxy.py +0 -0
  85. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/tools/settings.py +0 -0
  86. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/tools/tests/__init__.py +0 -0
  87. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/claude/tools/tests/test_computer.py +0 -0
  88. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/__init__.py +0 -0
  89. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/settings.py +0 -0
  90. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/tools/__init__.py +0 -0
  91. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/tools/base.py +0 -0
  92. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/tools/coding.py +0 -0
  93. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/tools/computer.py +0 -0
  94. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/tools/filesystem.py +0 -0
  95. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/tools/hosted.py +0 -0
  96. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/tools/mcp_proxy.py +0 -0
  97. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/tools/tests/__init__.py +0 -0
  98. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/gemini/tools/tests/test_computer.py +0 -0
  99. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/misc/__init__.py +0 -0
  100. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/misc/response_automation.py +0 -0
  101. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/__init__.py +0 -0
  102. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/tools/__init__.py +0 -0
  103. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/tools/apply_patch.py +0 -0
  104. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/tools/base.py +0 -0
  105. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/tools/coding.py +0 -0
  106. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/tools/computer.py +0 -0
  107. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/tools/hosted.py +0 -0
  108. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/tools/mcp_proxy.py +0 -0
  109. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/tools/strict_schema.py +0 -0
  110. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/tools/tests/__init__.py +0 -0
  111. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/tools/tests/test_computer.py +0 -0
  112. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai/tools/tests/test_strict_schema.py +0 -0
  113. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai_compatible/__init__.py +0 -0
  114. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai_compatible/tools/__init__.py +0 -0
  115. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai_compatible/tools/base.py +0 -0
  116. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/openai_compatible/tools/mcp_proxy.py +0 -0
  117. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/robot/__init__.py +0 -0
  118. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/robot/adapter.py +0 -0
  119. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/robot/dataset.py +0 -0
  120. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/robot/model.py +0 -0
  121. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tests/__init__.py +0 -0
  122. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tests/test_apply_patch.py +0 -0
  123. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tests/test_gemini_agent.py +0 -0
  124. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tests/test_openai_agent.py +0 -0
  125. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tests/test_robot_dataset_writer.py +0 -0
  126. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tests/test_trace.py +0 -0
  127. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tools/__init__.py +0 -0
  128. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tools/base.py +0 -0
  129. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tools/hosted.py +0 -0
  130. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tools/mcp.py +0 -0
  131. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/agents/tools/rfb.py +0 -0
  132. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/capabilities/cdp.py +0 -0
  133. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/capabilities/mcp.py +0 -0
  134. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/capabilities/rfb.py +0 -0
  135. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/capabilities/robot.py +0 -0
  136. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/capabilities/tests/__init__.py +0 -0
  137. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/capabilities/tests/test_rfb.py +0 -0
  138. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/__init__.py +0 -0
  139. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/__main__.py +0 -0
  140. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/deploy.py +0 -0
  141. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/init.py +0 -0
  142. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/jobs.py +0 -0
  143. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/models.py +0 -0
  144. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/project.py +0 -0
  145. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/qa.py +0 -0
  146. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/serve.py +0 -0
  147. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/sync.py +0 -0
  148. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/task.py +0 -0
  149. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/__init__.py +0 -0
  150. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/test_cli.py +0 -0
  151. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/test_deploy.py +0 -0
  152. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/test_init.py +0 -0
  153. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/test_jobs.py +0 -0
  154. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/test_main.py +0 -0
  155. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/test_project.py +0 -0
  156. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/test_qa.py +0 -0
  157. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/test_sync.py +0 -0
  158. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/test_task.py +0 -0
  159. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/tests/test_trace.py +0 -0
  160. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/cli/trace.py +0 -0
  161. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/clients/__init__.py +0 -0
  162. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/clients/tests/__init__.py +0 -0
  163. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/conftest.py +0 -0
  164. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/arguments.py +0 -0
  165. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/file_tracker.py +0 -0
  166. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/namespace.py +0 -0
  167. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/robot/__init__.py +0 -0
  168. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/robot/bridge.py +0 -0
  169. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/robot/endpoint.py +0 -0
  170. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/robot/gym.py +0 -0
  171. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/__init__.py +0 -0
  172. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/conftest.py +0 -0
  173. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_arguments.py +0 -0
  174. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_capability_backing.py +0 -0
  175. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_file_tracker.py +0 -0
  176. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_file_tracking.py +0 -0
  177. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_loader.py +0 -0
  178. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_manifest.py +0 -0
  179. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_platform.py +0 -0
  180. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_robot_regressions.py +0 -0
  181. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_server.py +0 -0
  182. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/tests/test_tunnel.py +0 -0
  183. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/environment/utils.py +0 -0
  184. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/__init__.py +0 -0
  185. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/chat.py +0 -0
  186. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/docker-seccomp.json +0 -0
  187. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/file_tracking.py +0 -0
  188. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/job.py +0 -0
  189. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/runtime/core.py +0 -0
  190. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/runtime/daytona.py +0 -0
  191. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/runtime/hud.py +0 -0
  192. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/runtime/local.py +0 -0
  193. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/runtime/modal.py +0 -0
  194. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/sync.py +0 -0
  195. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/task.py +0 -0
  196. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/taskset.py +0 -0
  197. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/tests/__init__.py +0 -0
  198. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/tests/test_chat.py +0 -0
  199. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/tests/test_file_tracking_observer.py +0 -0
  200. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/tests/test_job.py +0 -0
  201. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/tests/test_shared.py +0 -0
  202. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/eval/tests/test_sync.py +0 -0
  203. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/graders/__init__.py +0 -0
  204. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/graders/base.py +0 -0
  205. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/graders/bash.py +0 -0
  206. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/graders/combine.py +0 -0
  207. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/graders/judge.py +0 -0
  208. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/graders/results.py +0 -0
  209. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/graders/text.py +0 -0
  210. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/__init__.py +0 -0
  211. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/Dockerfile +0 -0
  212. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/__init__.py +0 -0
  213. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/adapt.py +0 -0
  214. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/env.py +0 -0
  215. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/export.py +0 -0
  216. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/install.sh +0 -0
  217. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/__init__.py +0 -0
  218. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/conftest.py +0 -0
  219. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/agent-lifecycle/environment/Dockerfile +0 -0
  220. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/agent-lifecycle/instruction.md +0 -0
  221. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/agent-lifecycle/solution/solve.sh +0 -0
  222. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/agent-lifecycle/task.toml +0 -0
  223. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/agent-lifecycle/tests/test.sh +0 -0
  224. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/Dockerfile +0 -0
  225. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/docker-compose.yaml +0 -0
  226. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/Dockerfile +0 -0
  227. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/__init__.py +0 -0
  228. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/server.py +0 -0
  229. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/hello-mcp/instruction.md +0 -0
  230. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/hello-mcp/solution/solve.sh +0 -0
  231. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/hello-mcp/task.toml +0 -0
  232. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/hello-mcp/tests/test.sh +0 -0
  233. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/phase-boundary/environment/Dockerfile +0 -0
  234. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/phase-boundary/environment/entrypoint.sh +0 -0
  235. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/phase-boundary/instruction.md +0 -0
  236. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/phase-boundary/solution/solve.sh +0 -0
  237. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/phase-boundary/task.toml +0 -0
  238. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/phase-boundary/tests/test.sh +0 -0
  239. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/sidecar-reachability/environment/Dockerfile +0 -0
  240. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/sidecar-reachability/environment/docker-compose.yaml +0 -0
  241. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/sidecar-reachability/instruction.md +0 -0
  242. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/sidecar-reachability/solution/solve.sh +0 -0
  243. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/sidecar-reachability/task.toml +0 -0
  244. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/sidecar-reachability/tests/Dockerfile +0 -0
  245. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/sidecar-reachability/tests/test.sh +0 -0
  246. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/environment/Dockerfile +0 -0
  247. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/environment/entrypoint.sh +0 -0
  248. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/instruction.md +0 -0
  249. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/solution/solve.sh +0 -0
  250. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/task.toml +0 -0
  251. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/tests/test.sh +0 -0
  252. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/test_contract.py +0 -0
  253. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/integrations/harbor/tests/test_harbor.py +0 -0
  254. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/patches/__init__.py +0 -0
  255. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/patches/mcp_patches.py +0 -0
  256. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/patches/tests/__init__.py +0 -0
  257. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/patches/tests/test_warnings.py +0 -0
  258. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/patches/warnings.py +0 -0
  259. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/py.typed +0 -0
  260. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/settings.py +0 -0
  261. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/telemetry/__init__.py +0 -0
  262. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/telemetry/context.py +0 -0
  263. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/telemetry/instrument.py +0 -0
  264. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/telemetry/robot/__init__.py +0 -0
  265. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/telemetry/robot/recorder.py +0 -0
  266. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/telemetry/robot/video.py +0 -0
  267. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/telemetry/span.py +0 -0
  268. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/telemetry/tests/__init__.py +0 -0
  269. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/telemetry/tests/test_instrument.py +0 -0
  270. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/train/__init__.py +0 -0
  271. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/train/base.py +0 -0
  272. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/train/client.py +0 -0
  273. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/train/tests/__init__.py +0 -0
  274. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/train/tests/test_client.py +0 -0
  275. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/train/types.py +0 -0
  276. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/__init__.py +0 -0
  277. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/docker.py +0 -0
  278. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/exceptions.py +0 -0
  279. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/hints.py +0 -0
  280. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/hud_console.py +0 -0
  281. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/modules.py +0 -0
  282. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/naming.py +0 -0
  283. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/platform.py +0 -0
  284. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/process.py +0 -0
  285. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/requests.py +0 -0
  286. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/serialization.py +0 -0
  287. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/tests/__init__.py +0 -0
  288. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/tests/test_exceptions.py +0 -0
  289. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/tests/test_hints.py +0 -0
  290. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/tests/test_hud_console.py +0 -0
  291. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/tests/test_naming.py +0 -0
  292. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/tests/test_platform.py +0 -0
  293. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/tests/test_process.py +0 -0
  294. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/tests/test_requests.py +0 -0
  295. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/tests/test_serialization.py +0 -0
  296. {hud-0.6.18.dev2 → hud-0.6.19.dev2}/hud/utils/time.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hud
3
- Version: 0.6.18.dev2
3
+ Version: 0.6.19.dev2
4
4
  Summary: SDK for the HUD platform.
5
5
  Project-URL: Homepage, https://github.com/hud-evals/hud-python
6
6
  Project-URL: Bug Tracker, https://github.com/hud-evals/hud-python/issues
@@ -38,7 +38,7 @@ Requires-Dist: anthropic>=0.98.0
38
38
  Requires-Dist: asyncssh>=2.23.0
39
39
  Requires-Dist: asyncvnc>=1.3.0
40
40
  Requires-Dist: fastmcp==3.2.0
41
- Requires-Dist: google-genai
41
+ Requires-Dist: google-genai>=1.75.0
42
42
  Requires-Dist: httpx2<3,>=2.0.0
43
43
  Requires-Dist: httpx<1,>=0.23.0
44
44
  Requires-Dist: mcp<2.0,>=1.24.0
@@ -199,19 +199,6 @@ Each graded evaluation is a **trace** (the SDK's live handle is a `Run`). With `
199
199
 
200
200
  → [Quickstart](https://docs.hud.ai/v6/start/quickstart) · [Tasks & tasksets](https://docs.hud.ai/v6/reference/tasks)
201
201
 
202
- ## Existing benchmarks
203
-
204
- The experimental `hud.integrations.harbor` adapter turns Harbor task directories into runnable HUD
205
- tasksets and can export authored HUD tasks back to Harbor folders.
206
-
207
- ```python
208
- from hud.integrations import harbor
209
-
210
- taskset = await harbor.adapt("./benchmark")
211
- ```
212
-
213
- → [Harbor interop](https://docs.hud.ai/v6/advanced/harbor-convert)
214
-
215
202
  ## Capabilities & harnesses
216
203
 
217
204
  A **capability** is a connection the environment exposes; a **harness** attaches its own tools to it. The same environment serves a one-shot Q&A or a full computer-use rollout, depending on which capabilities the harness opens.
@@ -234,6 +221,17 @@ A **capability** is a connection the environment exposes; a **harness** attaches
234
221
 
235
222
  From the [platform UI](https://hud.ai) you can run batches, compare models on the same taskset, and inspect every trace.
236
223
 
224
+ Hosted Claude Code and Codex harnesses reach platform inference through an
225
+ environment-owned, workspace-local endpoint. The endpoint is available only to
226
+ `bwrap` workspaces with network isolation and is bound to the exact CLI process
227
+ selected by the harness. Platform credentials stay in the environment-owned
228
+ relay rather than the CLI environment, workspace manifest, or child processes.
229
+ Codex therefore uses the environment sandbox instead of starting a nested
230
+ Codex sandbox for process-bound hosted execution.
231
+ The workspace probes its substrate and uses either seccomp notification or a
232
+ ptrace-backed seccomp guard; it does not advertise process-bound connections
233
+ when neither enforcement backend is available.
234
+
237
235
  → [Run & deploy](https://docs.hud.ai/v6/reference/runtime)
238
236
 
239
237
  ## Train on rewards
@@ -113,19 +113,6 @@ Each graded evaluation is a **trace** (the SDK's live handle is a `Run`). With `
113
113
 
114
114
  → [Quickstart](https://docs.hud.ai/v6/start/quickstart) · [Tasks & tasksets](https://docs.hud.ai/v6/reference/tasks)
115
115
 
116
- ## Existing benchmarks
117
-
118
- The experimental `hud.integrations.harbor` adapter turns Harbor task directories into runnable HUD
119
- tasksets and can export authored HUD tasks back to Harbor folders.
120
-
121
- ```python
122
- from hud.integrations import harbor
123
-
124
- taskset = await harbor.adapt("./benchmark")
125
- ```
126
-
127
- → [Harbor interop](https://docs.hud.ai/v6/advanced/harbor-convert)
128
-
129
116
  ## Capabilities & harnesses
130
117
 
131
118
  A **capability** is a connection the environment exposes; a **harness** attaches its own tools to it. The same environment serves a one-shot Q&A or a full computer-use rollout, depending on which capabilities the harness opens.
@@ -148,6 +135,17 @@ A **capability** is a connection the environment exposes; a **harness** attaches
148
135
 
149
136
  From the [platform UI](https://hud.ai) you can run batches, compare models on the same taskset, and inspect every trace.
150
137
 
138
+ Hosted Claude Code and Codex harnesses reach platform inference through an
139
+ environment-owned, workspace-local endpoint. The endpoint is available only to
140
+ `bwrap` workspaces with network isolation and is bound to the exact CLI process
141
+ selected by the harness. Platform credentials stay in the environment-owned
142
+ relay rather than the CLI environment, workspace manifest, or child processes.
143
+ Codex therefore uses the environment sandbox instead of starting a nested
144
+ Codex sandbox for process-bound hosted execution.
145
+ The workspace probes its substrate and uses either seccomp notification or a
146
+ ptrace-backed seccomp guard; it does not advertise process-bound connections
147
+ when neither enforcement backend is available.
148
+
151
149
  → [Run & deploy](https://docs.hud.ai/v6/reference/runtime)
152
150
 
153
151
  ## Train on rewards
@@ -7,6 +7,7 @@ from __future__ import annotations
7
7
 
8
8
  # Apply patches to third-party libraries early, before other imports
9
9
  from . import patches as _patches # noqa: F401
10
+ from .capabilities import Connection
10
11
  from .clients import connect
11
12
  from .environment import Environment
12
13
  from .eval import (
@@ -38,6 +39,7 @@ from .version import __version__
38
39
  __all__ = [
39
40
  "Chat",
40
41
  "ComposeProject",
42
+ "Connection",
41
43
  "DockerRuntime",
42
44
  "Environment",
43
45
  "Grade",
@@ -1,7 +1,4 @@
1
- """Agent implementations.
2
-
3
- The robot policy harness lives in :mod:`hud.agents.robot` (requires the ``robot`` extra).
4
- """
1
+ """Agent implementations."""
5
2
 
6
3
  from __future__ import annotations
7
4
 
@@ -15,13 +12,16 @@ from hud.utils.gateway import resolve_gateway_model
15
12
  if TYPE_CHECKING:
16
13
  from typing import TypeAlias
17
14
 
18
- from hud.agents.claude import ClaudeAgent, ClaudeSDKAgent, ClaudeSDKConfig
15
+ from hud.agents.claude import ClaudeAgent, ClaudeCLIAgent, ClaudeCLIConfig
16
+ from hud.agents.codex import CodexCLIAgent, CodexCLIConfig
19
17
  from hud.agents.gemini import GeminiAgent
20
18
  from hud.agents.openai import OpenAIAgent
21
19
  from hud.agents.openai_compatible import OpenAIChatAgent
22
20
  from hud.agents.tool_agent import ToolAgent as MCPAgent
23
21
 
24
- GatewayAgent: TypeAlias = ClaudeAgent | GeminiAgent | OpenAIAgent | OpenAIChatAgent
22
+ GatewayAgent: TypeAlias = (
23
+ ClaudeAgent | ClaudeCLIAgent | CodexCLIAgent | GeminiAgent | OpenAIAgent | OpenAIChatAgent
24
+ )
25
25
 
26
26
 
27
27
  def create_agent(model: str, **kwargs: Any) -> GatewayAgent:
@@ -46,44 +46,22 @@ def create_agent(model: str, **kwargs: Any) -> GatewayAgent:
46
46
  if not settings.api_key:
47
47
  raise HudAuthenticationError("HUD_API_KEY is required to create a gateway agent")
48
48
 
49
- agent_type, model_id = resolve_agent_model(model)
50
- kwargs.setdefault("model", model_id)
51
- kwargs["gateway"] = True
52
- # cls/config_cls are matched unions; the pairing is correct by construction.
53
- config = agent_type.config_cls(**kwargs)
54
- return agent_type.cls(cast("Any", config))
55
-
56
-
57
- def resolve_agent_model(model: str) -> tuple[AgentType, str]:
58
- """Resolve a catalog id/name/slug or an agent type without constructing a client."""
59
- agent_type = next((candidate for candidate in AgentType if candidate.value == model), None)
60
- if agent_type is not None:
61
- return agent_type, model
62
- try:
49
+ if model in {member.value for member in AgentType}:
50
+ agent_type = AgentType(model)
51
+ else:
63
52
  entry = resolve_gateway_model(model)
64
- except HudAuthenticationError:
65
- types = ", ".join(candidate.value for candidate in AgentType)
66
- raise ValueError(
67
- f"Resolving {model!r} needs the HUD model catalog; set HUD_API_KEY, or pass an "
68
- f"agent type ({types}) and choose the model with its config."
69
- ) from None
70
- if entry.sdk_agent_type in ("operator", "gemini_cua"):
71
- replacement = "openai" if entry.sdk_agent_type == "operator" else "gemini"
72
- raise ValueError(
73
- f"The {entry.sdk_agent_type} agent is no longer supported; use {replacement} with a "
74
- "supported computer-use model."
75
- )
76
- try:
77
53
  agent_type = AgentType(entry.sdk_agent_type)
78
- except ValueError as exc:
79
- raise ValueError(f"Model {model!r} has invalid agent type metadata") from exc
80
- return agent_type, entry.model_name or model
54
+ kwargs.setdefault("model", entry.model_name or model)
55
+ kwargs["gateway"] = True
56
+ return cast("GatewayAgent", agent_type.cls.load(kwargs))
81
57
 
82
58
 
83
59
  _LAZY_EXPORTS = {
84
60
  "ClaudeAgent": ("hud.agents.claude", "ClaudeAgent"),
85
- "ClaudeSDKAgent": ("hud.agents.claude", "ClaudeSDKAgent"),
86
- "ClaudeSDKConfig": ("hud.agents.claude", "ClaudeSDKConfig"),
61
+ "ClaudeCLIAgent": ("hud.agents.claude", "ClaudeCLIAgent"),
62
+ "ClaudeCLIConfig": ("hud.agents.claude", "ClaudeCLIConfig"),
63
+ "CodexCLIAgent": ("hud.agents.codex", "CodexCLIAgent"),
64
+ "CodexCLIConfig": ("hud.agents.codex", "CodexCLIConfig"),
87
65
  "GeminiAgent": ("hud.agents.gemini", "GeminiAgent"),
88
66
  "MCPAgent": ("hud.agents.tool_agent", "ToolAgent"),
89
67
  "OpenAIAgent": ("hud.agents.openai", "OpenAIAgent"),
@@ -92,14 +70,15 @@ _LAZY_EXPORTS = {
92
70
 
93
71
  __all__ = [
94
72
  "ClaudeAgent",
95
- "ClaudeSDKAgent",
96
- "ClaudeSDKConfig",
73
+ "ClaudeCLIAgent",
74
+ "ClaudeCLIConfig",
75
+ "CodexCLIAgent",
76
+ "CodexCLIConfig",
97
77
  "GeminiAgent",
98
78
  "MCPAgent",
99
79
  "OpenAIAgent",
100
80
  "OpenAIChatAgent",
101
81
  "create_agent",
102
- "resolve_agent_model",
103
82
  ]
104
83
 
105
84
 
@@ -0,0 +1,47 @@
1
+ """Agent ABC: the rollout contract."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import TYPE_CHECKING, ClassVar, Generic, Self, cast
7
+
8
+ from typing_extensions import TypeVar
9
+
10
+ from hud.agents.types import AgentConfig
11
+
12
+ if TYPE_CHECKING:
13
+ from hud.eval.run import Run
14
+ from hud.utils.serialization import JsonObject
15
+
16
+ ConfigT_co = TypeVar("ConfigT_co", bound=AgentConfig, covariant=True, default=AgentConfig)
17
+
18
+
19
+ class Agent(ABC, Generic[ConfigT_co]):
20
+ """Drives a live ``Run`` by recording its trajectory and final answer.
21
+
22
+ Subclasses implement ``__call__(run)``; callers do ``await agent(run)``. Stateless
23
+ per run — everything comes from ``run`` — so one instance drives many concurrent
24
+ rollouts. The caller owns lifecycle status, cancellation, and grading.
25
+
26
+ ``config`` is the agent's serializable identity: :meth:`dump` writes it and
27
+ :meth:`load` rebuilds the agent from it. Subclasses parametrize ``Agent[XConfig]``,
28
+ set ``config_cls = XConfig`` for construction, and, when they override
29
+ ``__init__``, call ``super().__init__(config)``.
30
+ """
31
+
32
+ config_cls: ClassVar[type[AgentConfig]] = AgentConfig
33
+ config: ConfigT_co
34
+
35
+ def __init__(self, config: ConfigT_co | None = None) -> None:
36
+ self.config = config or cast("ConfigT_co", self.config_cls())
37
+
38
+ @abstractmethod
39
+ async def __call__(self, run: Run) -> None:
40
+ """Fill ``run.trace`` with the trajectory and final answer."""
41
+
42
+ def dump(self) -> JsonObject:
43
+ return self.config.model_dump(mode="json")
44
+
45
+ @classmethod
46
+ def load(cls, data: JsonObject) -> Self:
47
+ return cls(cast("ConfigT_co", cls.config_cls.model_validate(data)))
@@ -24,7 +24,6 @@ from urllib.parse import urlsplit, urlunsplit
24
24
  from hud.agents.base import Agent
25
25
  from hud.agents.types import AgentStep, BrowserUseConfig
26
26
  from hud.settings import settings
27
- from hud.types import Step
28
27
 
29
28
  if TYPE_CHECKING:
30
29
  from hud.eval.run import Run
@@ -72,13 +71,10 @@ class _BrowserUseModule(Protocol):
72
71
  Agent: _AgentFactory
73
72
 
74
73
 
75
- class BrowserUseAgent(Agent):
74
+ class BrowserUseAgent(Agent[BrowserUseConfig]):
76
75
  """Run the ``browser-use`` agent against an env's ``cdp/1.3`` capability."""
77
76
 
78
- config: BrowserUseConfig
79
-
80
- def __init__(self, config: BrowserUseConfig | None = None) -> None:
81
- self.config = config or BrowserUseConfig()
77
+ config_cls = BrowserUseConfig
82
78
 
83
79
  async def __call__(self, run: Run) -> None:
84
80
  """Drive browser-use over the run's CDP capability, filling ``run.trace``.
@@ -107,18 +103,12 @@ class BrowserUseAgent(Agent):
107
103
 
108
104
  try:
109
105
  history = await sdk_agent.run(max_steps=self.config.max_steps)
110
- except Exception as exc:
111
- LOGGER.exception("browser-use run failed")
112
- trace.status = "error"
113
- run.record(Step(source="system", error=str(exc)))
114
- return
115
106
  finally:
116
107
  with contextlib.suppress(Exception):
117
108
  await browser.stop()
118
109
 
119
110
  successful = history.is_successful()
120
111
  content = history.final_result() or ""
121
- trace.status = "error" if successful is False else "completed"
122
112
  trace.content = content
123
113
  trace.extra.update(
124
114
  {
@@ -133,7 +123,6 @@ class BrowserUseAgent(Agent):
133
123
  AgentStep(
134
124
  content=content,
135
125
  done=history.is_done(),
136
- error=content if successful is False else None,
137
126
  ),
138
127
  )
139
128
 
@@ -7,15 +7,15 @@ from .agent import (
7
7
  AsyncAnthropicBedrock,
8
8
  ClaudeAgent,
9
9
  )
10
- from .sdk import ClaudeSDKAgent, ClaudeSDKConfig
10
+ from .sdk import ClaudeCLIAgent, ClaudeCLIConfig
11
11
  from .tools import ClaudeToolSearchTool, ClaudeWebFetchTool, ClaudeWebSearchTool
12
12
 
13
13
  __all__ = [
14
14
  "AsyncAnthropic",
15
15
  "AsyncAnthropicBedrock",
16
16
  "ClaudeAgent",
17
- "ClaudeSDKAgent",
18
- "ClaudeSDKConfig",
17
+ "ClaudeCLIAgent",
18
+ "ClaudeCLIConfig",
19
19
  "ClaudeToolSearchTool",
20
20
  "ClaudeWebFetchTool",
21
21
  "ClaudeWebSearchTool",
@@ -8,7 +8,7 @@ import json
8
8
  import logging
9
9
  import math
10
10
  from random import SystemRandom
11
- from typing import TYPE_CHECKING, Literal, cast
11
+ from typing import TYPE_CHECKING, cast
12
12
 
13
13
  import httpx
14
14
  import httpx2
@@ -23,7 +23,6 @@ from anthropic import (
23
23
  )
24
24
  from anthropic.types import CacheControlEphemeralParam
25
25
  from anthropic.types.beta import (
26
- BetaBase64ImageSourceParam,
27
26
  BetaBase64PDFSourceParam,
28
27
  BetaImageBlockParam,
29
28
  BetaMessage,
@@ -35,12 +34,14 @@ from anthropic.types.beta import (
35
34
  BetaToolResultBlockParam,
36
35
  BetaToolUnionParam,
37
36
  )
37
+ from PIL import Image
38
38
 
39
39
  from hud.agents.tool_agent import RunState, ToolAgent
40
40
  from hud.agents.types import AgentStep, Citation, ClaudeConfig, Usage
41
41
  from hud.types import MCPToolCall, MCPToolResult
42
42
  from hud.utils import gateway
43
43
 
44
+ from .images import image_source
44
45
  from .tools.coding import ClaudeBashTool, ClaudeTextEditorTool
45
46
  from .tools.computer import ClaudeComputerTool
46
47
  from .tools.mcp_proxy import ClaudeMCPProxyTool
@@ -48,9 +49,9 @@ from .tools.mcp_proxy import ClaudeMCPProxyTool
48
49
  if TYPE_CHECKING:
49
50
  from anthropic.types.beta import BetaTextCitation
50
51
 
52
+
51
53
  logger = logging.getLogger(__name__)
52
54
 
53
- ClaudeImageMediaType = Literal["image/jpeg", "image/png", "image/gif", "image/webp"]
54
55
  ClaudeToolResultContent = BetaTextBlockParam | BetaImageBlockParam | BetaRequestDocumentBlockParam
55
56
 
56
57
  _RETRYABLE_STREAM_ERROR_TYPES = frozenset(
@@ -93,6 +94,7 @@ def _stream_retry_delay(attempt: int, error: Exception) -> float:
93
94
  class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
94
95
  """Anthropic Claude agent. Drives SSH (coding), RFB (computer), and MCP capabilities."""
95
96
 
97
+ config_cls = ClaudeConfig
96
98
  tool_catalog = (
97
99
  ClaudeBashTool,
98
100
  ClaudeTextEditorTool,
@@ -101,7 +103,7 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
101
103
  )
102
104
 
103
105
  def __init__(self, config: ClaudeConfig | None = None) -> None:
104
- self.config = config or ClaudeConfig()
106
+ super().__init__(config)
105
107
  self.anthropic_client: AsyncAnthropic | AsyncAnthropicBedrock = self._resolve_client()
106
108
 
107
109
  def _resolve_client(self) -> AsyncAnthropic | AsyncAnthropicBedrock:
@@ -138,6 +140,7 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
138
140
  return None
139
141
 
140
142
  result_content = result.content
143
+ image_error = False
141
144
  if result.isError:
142
145
  error_msg = next(
143
146
  (c.text for c in result.content if isinstance(c, mcp_types.TextContent)),
@@ -166,14 +169,19 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
166
169
  citations={"enabled": True},
167
170
  )
168
171
  case mcp_types.ImageContent():
169
- block = BetaImageBlockParam(
170
- type="image",
171
- source=BetaBase64ImageSourceParam(
172
- type="base64",
173
- media_type=cast("ClaudeImageMediaType", content.mimeType),
174
- data=content.data,
175
- ),
176
- )
172
+ try:
173
+ block = BetaImageBlockParam(
174
+ type="image",
175
+ source=image_source(
176
+ content,
177
+ preserve_dimensions=isinstance(
178
+ state.tools.get(call.name), ClaudeComputerTool
179
+ ),
180
+ ),
181
+ )
182
+ except (ValueError, OSError, Image.DecompressionBombError) as exc:
183
+ image_error = True
184
+ block = BetaTextBlockParam(type="text", text=f"Cannot view image: {exc}")
177
185
  case mcp_types.EmbeddedResource(
178
186
  resource=mcp_types.BlobResourceContents(mimeType="application/pdf") as resource,
179
187
  ):
@@ -205,6 +213,7 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
205
213
  type="tool_result",
206
214
  tool_use_id=tool_use_id,
207
215
  content=claude_blocks,
216
+ is_error=bool(result.isError) or image_error,
208
217
  ),
209
218
  ],
210
219
  )
@@ -360,26 +369,36 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
360
369
  if response is None:
361
370
  raise ValueError("Claude response missing after retries")
362
371
 
363
- result = AgentStep(content="", done=True)
364
- result.model = response.model
365
- result.usage = Usage(
366
- prompt_tokens=response.usage.input_tokens,
367
- completion_tokens=response.usage.output_tokens,
368
- cached_tokens=response.usage.cache_read_input_tokens,
372
+ return self.message_to_agent_step(response, citations_enabled=citations_enabled)
373
+
374
+ @classmethod
375
+ def message_to_agent_step(
376
+ cls,
377
+ response: BetaMessage,
378
+ *,
379
+ citations_enabled: bool = False,
380
+ ) -> AgentStep:
381
+ result = AgentStep(
382
+ content="",
383
+ done=True,
384
+ model=response.model,
385
+ usage=Usage(
386
+ prompt_tokens=response.usage.input_tokens,
387
+ completion_tokens=response.usage.output_tokens,
388
+ cached_tokens=response.usage.cache_read_input_tokens,
389
+ ),
369
390
  )
370
391
  text_parts: list[str] = []
371
392
  thinking_parts: list[str] = []
372
- citations: list[Citation] = []
373
393
 
374
394
  for block in response.content:
375
395
  match block.type:
376
396
  case "tool_use":
377
- arguments = dict(block.input) if block.input else {}
378
397
  result.tool_calls.append(
379
398
  MCPToolCall(
380
399
  id=block.id,
381
400
  name=block.name,
382
- arguments=arguments,
401
+ arguments=dict(block.input) if block.input else {},
383
402
  _meta=mcp_types.RequestParams.Meta.model_validate(
384
403
  {"citations_enabled": citations_enabled},
385
404
  ),
@@ -387,9 +406,8 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
387
406
  )
388
407
  result.done = False
389
408
  case "text":
390
- text_block = block
391
- text_parts.append(text_block.text)
392
- citations.extend(self._citation(c) for c in (text_block.citations or []))
409
+ text_parts.append(block.text)
410
+ result.citations.extend(cls._citation(c) for c in (block.citations or []))
393
411
  case "thinking":
394
412
  if block.thinking:
395
413
  thinking_parts.append(block.thinking)
@@ -397,7 +415,6 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
397
415
  pass
398
416
 
399
417
  result.content = "".join(text_parts)
400
- result.citations = citations
401
418
  if thinking_parts:
402
419
  result.reasoning = "\n".join(thinking_parts)
403
420
  result.finish_reason = response.stop_reason
@@ -0,0 +1,65 @@
1
+ """Image input preparation for Anthropic Messages."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ import math
7
+ from io import BytesIO
8
+ from typing import TYPE_CHECKING, Literal, cast
9
+
10
+ from anthropic.types.beta import BetaBase64ImageSourceParam
11
+ from PIL import Image, ImageOps
12
+
13
+ if TYPE_CHECKING:
14
+ from mcp.types import ImageContent
15
+
16
+ ClaudeImageMediaType = Literal["image/jpeg", "image/png", "image/gif", "image/webp"]
17
+ MAX_SOURCE_IMAGE_PIXELS = 16_000_000
18
+ MAX_IMAGE_EDGE = 1568
19
+ MAX_IMAGE_PIXELS = 1_150_000
20
+ MAX_IMAGE_BASE64_BYTES = 5_000_000
21
+
22
+
23
+ def image_source(
24
+ content: ImageContent, *, preserve_dimensions: bool = False
25
+ ) -> BetaBase64ImageSourceParam:
26
+ """Prepare a tool image, preserving computer-tool coordinate space when requested."""
27
+ if preserve_dimensions:
28
+ return BetaBase64ImageSourceParam(
29
+ type="base64",
30
+ media_type=cast("ClaudeImageMediaType", content.mimeType),
31
+ data=content.data,
32
+ )
33
+ with Image.open(BytesIO(base64.b64decode(content.data, validate=True))) as image:
34
+ if image.width * image.height > MAX_SOURCE_IMAGE_PIXELS:
35
+ raise ValueError(
36
+ f"Image exceeds the {MAX_SOURCE_IMAGE_PIXELS:,}-pixel source limit. "
37
+ "Resize or crop it before viewing."
38
+ )
39
+ return _encode_image(image)
40
+
41
+
42
+ def _encode_image(image: Image.Image) -> BetaBase64ImageSourceParam:
43
+ scale = min(
44
+ 1.0,
45
+ MAX_IMAGE_EDGE / max(image.size),
46
+ math.sqrt(MAX_IMAGE_PIXELS / (image.width * image.height)),
47
+ )
48
+ image.thumbnail(
49
+ (max(1, int(image.width * scale)), max(1, int(image.height * scale))),
50
+ Image.Resampling.LANCZOS,
51
+ )
52
+ image = ImageOps.exif_transpose(image)
53
+ image = image.convert("RGBA" if image.has_transparency_data else "RGB")
54
+ buffer = BytesIO()
55
+ image.save(buffer, format="PNG", optimize=True)
56
+ data = base64.b64encode(buffer.getvalue()).decode("ascii")
57
+ mime_type: ClaudeImageMediaType = "image/png"
58
+ if len(data) > MAX_IMAGE_BASE64_BYTES:
59
+ background = Image.new("RGB", image.size, "white")
60
+ background.paste(image, mask=image.getchannel("A") if image.mode == "RGBA" else None)
61
+ buffer = BytesIO()
62
+ background.save(buffer, format="JPEG", quality=85, optimize=True)
63
+ data = base64.b64encode(buffer.getvalue()).decode("ascii")
64
+ mime_type = "image/jpeg"
65
+ return BetaBase64ImageSourceParam(type="base64", media_type=mime_type, data=data)
@@ -0,0 +1,7 @@
1
+ """Agent that runs the ``claude`` CLI over SSH."""
2
+
3
+ from hud.agents.types import ClaudeCLIConfig
4
+
5
+ from .agent import ClaudeCLIAgent
6
+
7
+ __all__ = ["ClaudeCLIAgent", "ClaudeCLIConfig"]