hud 0.6.18.dev2__tar.gz → 0.6.19.dev1__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 (294) hide show
  1. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/PKG-INFO +13 -15
  2. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/README.md +11 -13
  3. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/__init__.py +2 -0
  4. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/__init__.py +20 -41
  5. hud-0.6.19.dev1/hud/agents/base.py +47 -0
  6. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/browser_use/agent.py +2 -13
  7. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/__init__.py +3 -3
  8. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/agent.py +24 -14
  9. hud-0.6.19.dev1/hud/agents/claude/sdk/__init__.py +7 -0
  10. hud-0.6.19.dev1/hud/agents/claude/sdk/agent.py +219 -0
  11. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/sdk/computer_mcp.py +50 -108
  12. hud-0.6.19.dev1/hud/agents/claude/sdk/events.py +128 -0
  13. hud-0.6.19.dev1/hud/agents/cli.py +141 -0
  14. hud-0.6.19.dev1/hud/agents/codex/__init__.py +7 -0
  15. hud-0.6.19.dev1/hud/agents/codex/agent.py +336 -0
  16. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/agent.py +4 -2
  17. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/agent.py +4 -2
  18. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai_compatible/agent.py +14 -19
  19. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/robot/agent.py +0 -1
  20. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/robot/batching.py +1 -0
  21. hud-0.6.19.dev1/hud/agents/tests/cli_fakes.py +111 -0
  22. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tests/test_base.py +37 -14
  23. hud-0.6.19.dev1/hud/agents/tests/test_claude_cli_agent.py +835 -0
  24. hud-0.6.19.dev1/hud/agents/tests/test_codex_cli_agent.py +381 -0
  25. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tests/test_openai_compatible_agent.py +4 -3
  26. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tests/test_tool_agent.py +38 -19
  27. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tool_agent.py +33 -92
  28. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/types.py +58 -33
  29. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/capabilities/__init__.py +2 -0
  30. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/capabilities/base.py +3 -0
  31. hud-0.6.19.dev1/hud/capabilities/connection.py +106 -0
  32. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/capabilities/ssh.py +38 -4
  33. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/capabilities/tests/test_ssh.py +51 -2
  34. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/eval.py +9 -10
  35. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/test_eval.py +3 -2
  36. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/clients/client.py +42 -7
  37. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/clients/tests/test_connect.py +85 -1
  38. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/__init__.py +2 -1
  39. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/egress.py +241 -1
  40. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/env.py +168 -1
  41. hud-0.6.19.dev1/hud/environment/process_guard.py +723 -0
  42. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/server.py +45 -2
  43. hud-0.6.19.dev1/hud/environment/tests/test_process_connection.py +368 -0
  44. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_sessions.py +73 -1
  45. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_workspace.py +323 -6
  46. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/workspace.py +234 -27
  47. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/run.py +49 -30
  48. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/runtime/__init__.py +2 -1
  49. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/runtime/compose.py +49 -4
  50. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/runtime/docker.py +8 -1
  51. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/runtime/hosted.py +7 -13
  52. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/tests/test_docker_provider.py +88 -0
  53. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/tests/test_hosted.py +52 -20
  54. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/tests/test_local_runtime.py +1 -0
  55. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/tests/test_rollout.py +25 -1
  56. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/tests/test_task.py +8 -4
  57. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/test_integration.py +1 -0
  58. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/telemetry/exporter.py +47 -29
  59. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/telemetry/tests/test_exporter.py +24 -0
  60. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/types.py +40 -19
  61. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/gateway.py +27 -11
  62. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/tests/test_gateway.py +18 -2
  63. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/version.py +1 -1
  64. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/pyproject.toml +1 -1
  65. hud-0.6.18.dev2/hud/agents/base.py +0 -22
  66. hud-0.6.18.dev2/hud/agents/claude/sdk/__init__.py +0 -5
  67. hud-0.6.18.dev2/hud/agents/claude/sdk/agent.py +0 -355
  68. hud-0.6.18.dev2/hud/agents/tests/test_claude_sdk_agent.py +0 -679
  69. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/.gitignore +0 -0
  70. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/LICENSE +0 -0
  71. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/__main__.py +0 -0
  72. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/browser_use/__init__.py +0 -0
  73. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/tools/__init__.py +0 -0
  74. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/tools/base.py +0 -0
  75. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/tools/coding.py +0 -0
  76. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/tools/computer.py +0 -0
  77. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/tools/hosted.py +0 -0
  78. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/tools/mcp_proxy.py +0 -0
  79. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/tools/settings.py +0 -0
  80. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/tools/tests/__init__.py +0 -0
  81. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/claude/tools/tests/test_computer.py +0 -0
  82. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/__init__.py +0 -0
  83. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/settings.py +0 -0
  84. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/tools/__init__.py +0 -0
  85. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/tools/base.py +0 -0
  86. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/tools/coding.py +0 -0
  87. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/tools/computer.py +0 -0
  88. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/tools/filesystem.py +0 -0
  89. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/tools/hosted.py +0 -0
  90. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/tools/mcp_proxy.py +0 -0
  91. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/tools/tests/__init__.py +0 -0
  92. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/gemini/tools/tests/test_computer.py +0 -0
  93. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/misc/__init__.py +0 -0
  94. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/misc/response_automation.py +0 -0
  95. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/__init__.py +0 -0
  96. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/tools/__init__.py +0 -0
  97. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/tools/apply_patch.py +0 -0
  98. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/tools/base.py +0 -0
  99. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/tools/coding.py +0 -0
  100. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/tools/computer.py +0 -0
  101. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/tools/hosted.py +0 -0
  102. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/tools/mcp_proxy.py +0 -0
  103. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/tools/strict_schema.py +0 -0
  104. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/tools/tests/__init__.py +0 -0
  105. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/tools/tests/test_computer.py +0 -0
  106. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai/tools/tests/test_strict_schema.py +0 -0
  107. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai_compatible/__init__.py +0 -0
  108. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai_compatible/tools/__init__.py +0 -0
  109. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai_compatible/tools/base.py +0 -0
  110. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai_compatible/tools/filesystem.py +0 -0
  111. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/openai_compatible/tools/mcp_proxy.py +0 -0
  112. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/robot/__init__.py +0 -0
  113. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/robot/adapter.py +0 -0
  114. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/robot/dataset.py +0 -0
  115. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/robot/model.py +0 -0
  116. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tests/__init__.py +0 -0
  117. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tests/test_apply_patch.py +0 -0
  118. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tests/test_claude_agent.py +0 -0
  119. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tests/test_gemini_agent.py +0 -0
  120. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tests/test_openai_agent.py +0 -0
  121. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tests/test_provider_native_tools.py +0 -0
  122. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tests/test_robot_dataset_writer.py +0 -0
  123. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tests/test_trace.py +0 -0
  124. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tools/__init__.py +0 -0
  125. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tools/base.py +0 -0
  126. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tools/hosted.py +0 -0
  127. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tools/mcp.py +0 -0
  128. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tools/rfb.py +0 -0
  129. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/agents/tools/ssh.py +0 -0
  130. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/capabilities/cdp.py +0 -0
  131. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/capabilities/mcp.py +0 -0
  132. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/capabilities/rfb.py +0 -0
  133. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/capabilities/robot.py +0 -0
  134. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/capabilities/tests/__init__.py +0 -0
  135. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/capabilities/tests/test_rfb.py +0 -0
  136. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/__init__.py +0 -0
  137. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/__main__.py +0 -0
  138. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/deploy.py +0 -0
  139. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/init.py +0 -0
  140. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/jobs.py +0 -0
  141. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/models.py +0 -0
  142. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/project.py +0 -0
  143. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/qa.py +0 -0
  144. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/serve.py +0 -0
  145. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/sync.py +0 -0
  146. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/task.py +0 -0
  147. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/__init__.py +0 -0
  148. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/test_cli.py +0 -0
  149. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/test_deploy.py +0 -0
  150. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/test_init.py +0 -0
  151. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/test_jobs.py +0 -0
  152. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/test_main.py +0 -0
  153. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/test_project.py +0 -0
  154. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/test_qa.py +0 -0
  155. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/test_sync.py +0 -0
  156. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/test_task.py +0 -0
  157. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/tests/test_trace.py +0 -0
  158. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/cli/trace.py +0 -0
  159. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/clients/__init__.py +0 -0
  160. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/clients/tests/__init__.py +0 -0
  161. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/conftest.py +0 -0
  162. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/arguments.py +0 -0
  163. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/file_tracker.py +0 -0
  164. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/namespace.py +0 -0
  165. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/robot/__init__.py +0 -0
  166. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/robot/bridge.py +0 -0
  167. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/robot/endpoint.py +0 -0
  168. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/robot/gym.py +0 -0
  169. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/__init__.py +0 -0
  170. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/conftest.py +0 -0
  171. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_arguments.py +0 -0
  172. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_capability_backing.py +0 -0
  173. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_file_tracker.py +0 -0
  174. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_file_tracking.py +0 -0
  175. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_loader.py +0 -0
  176. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_manifest.py +0 -0
  177. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_platform.py +0 -0
  178. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_robot_regressions.py +0 -0
  179. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_server.py +0 -0
  180. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/tests/test_tunnel.py +0 -0
  181. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/environment/utils.py +0 -0
  182. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/__init__.py +0 -0
  183. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/chat.py +0 -0
  184. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/docker-seccomp.json +0 -0
  185. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/file_tracking.py +0 -0
  186. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/job.py +0 -0
  187. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/runtime/core.py +0 -0
  188. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/runtime/daytona.py +0 -0
  189. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/runtime/hud.py +0 -0
  190. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/runtime/local.py +0 -0
  191. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/runtime/modal.py +0 -0
  192. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/sync.py +0 -0
  193. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/task.py +0 -0
  194. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/taskset.py +0 -0
  195. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/tests/__init__.py +0 -0
  196. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/tests/test_chat.py +0 -0
  197. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/tests/test_file_tracking_observer.py +0 -0
  198. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/tests/test_job.py +0 -0
  199. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/tests/test_shared.py +0 -0
  200. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/eval/tests/test_sync.py +0 -0
  201. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/graders/__init__.py +0 -0
  202. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/graders/base.py +0 -0
  203. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/graders/bash.py +0 -0
  204. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/graders/combine.py +0 -0
  205. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/graders/judge.py +0 -0
  206. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/graders/results.py +0 -0
  207. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/graders/text.py +0 -0
  208. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/__init__.py +0 -0
  209. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/Dockerfile +0 -0
  210. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/__init__.py +0 -0
  211. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/adapt.py +0 -0
  212. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/env.py +0 -0
  213. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/export.py +0 -0
  214. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/install.sh +0 -0
  215. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/__init__.py +0 -0
  216. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/conftest.py +0 -0
  217. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/agent-lifecycle/environment/Dockerfile +0 -0
  218. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/agent-lifecycle/instruction.md +0 -0
  219. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/agent-lifecycle/solution/solve.sh +0 -0
  220. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/agent-lifecycle/task.toml +0 -0
  221. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/agent-lifecycle/tests/test.sh +0 -0
  222. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/Dockerfile +0 -0
  223. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/docker-compose.yaml +0 -0
  224. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/Dockerfile +0 -0
  225. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/__init__.py +0 -0
  226. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/server.py +0 -0
  227. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/hello-mcp/instruction.md +0 -0
  228. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/hello-mcp/solution/solve.sh +0 -0
  229. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/hello-mcp/task.toml +0 -0
  230. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/hello-mcp/tests/test.sh +0 -0
  231. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/phase-boundary/environment/Dockerfile +0 -0
  232. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/phase-boundary/environment/entrypoint.sh +0 -0
  233. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/phase-boundary/instruction.md +0 -0
  234. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/phase-boundary/solution/solve.sh +0 -0
  235. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/phase-boundary/task.toml +0 -0
  236. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/phase-boundary/tests/test.sh +0 -0
  237. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/sidecar-reachability/environment/Dockerfile +0 -0
  238. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/sidecar-reachability/environment/docker-compose.yaml +0 -0
  239. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/sidecar-reachability/instruction.md +0 -0
  240. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/sidecar-reachability/solution/solve.sh +0 -0
  241. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/sidecar-reachability/task.toml +0 -0
  242. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/sidecar-reachability/tests/Dockerfile +0 -0
  243. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/sidecar-reachability/tests/test.sh +0 -0
  244. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/environment/Dockerfile +0 -0
  245. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/environment/entrypoint.sh +0 -0
  246. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/instruction.md +0 -0
  247. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/solution/solve.sh +0 -0
  248. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/task.toml +0 -0
  249. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/tests/test.sh +0 -0
  250. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/test_contract.py +0 -0
  251. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/integrations/harbor/tests/test_harbor.py +0 -0
  252. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/patches/__init__.py +0 -0
  253. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/patches/mcp_patches.py +0 -0
  254. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/patches/tests/__init__.py +0 -0
  255. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/patches/tests/test_warnings.py +0 -0
  256. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/patches/warnings.py +0 -0
  257. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/py.typed +0 -0
  258. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/settings.py +0 -0
  259. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/telemetry/__init__.py +0 -0
  260. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/telemetry/context.py +0 -0
  261. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/telemetry/instrument.py +0 -0
  262. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/telemetry/robot/__init__.py +0 -0
  263. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/telemetry/robot/recorder.py +0 -0
  264. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/telemetry/robot/video.py +0 -0
  265. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/telemetry/span.py +0 -0
  266. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/telemetry/tests/__init__.py +0 -0
  267. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/telemetry/tests/test_instrument.py +0 -0
  268. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/train/__init__.py +0 -0
  269. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/train/base.py +0 -0
  270. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/train/client.py +0 -0
  271. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/train/tests/__init__.py +0 -0
  272. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/train/tests/test_client.py +0 -0
  273. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/train/types.py +0 -0
  274. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/__init__.py +0 -0
  275. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/docker.py +0 -0
  276. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/exceptions.py +0 -0
  277. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/hints.py +0 -0
  278. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/hud_console.py +0 -0
  279. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/modules.py +0 -0
  280. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/naming.py +0 -0
  281. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/platform.py +0 -0
  282. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/process.py +0 -0
  283. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/requests.py +0 -0
  284. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/serialization.py +0 -0
  285. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/tests/__init__.py +0 -0
  286. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/tests/test_exceptions.py +0 -0
  287. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/tests/test_hints.py +0 -0
  288. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/tests/test_hud_console.py +0 -0
  289. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/tests/test_naming.py +0 -0
  290. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/tests/test_platform.py +0 -0
  291. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/tests/test_process.py +0 -0
  292. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/tests/test_requests.py +0 -0
  293. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/hud/utils/tests/test_serialization.py +0 -0
  294. {hud-0.6.18.dev2 → hud-0.6.19.dev1}/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.dev1
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",
@@ -48,6 +48,7 @@ from .tools.mcp_proxy import ClaudeMCPProxyTool
48
48
  if TYPE_CHECKING:
49
49
  from anthropic.types.beta import BetaTextCitation
50
50
 
51
+
51
52
  logger = logging.getLogger(__name__)
52
53
 
53
54
  ClaudeImageMediaType = Literal["image/jpeg", "image/png", "image/gif", "image/webp"]
@@ -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:
@@ -360,26 +362,36 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
360
362
  if response is None:
361
363
  raise ValueError("Claude response missing after retries")
362
364
 
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,
365
+ return self.message_to_agent_step(response, citations_enabled=citations_enabled)
366
+
367
+ @classmethod
368
+ def message_to_agent_step(
369
+ cls,
370
+ response: BetaMessage,
371
+ *,
372
+ citations_enabled: bool = False,
373
+ ) -> AgentStep:
374
+ result = AgentStep(
375
+ content="",
376
+ done=True,
377
+ model=response.model,
378
+ usage=Usage(
379
+ prompt_tokens=response.usage.input_tokens,
380
+ completion_tokens=response.usage.output_tokens,
381
+ cached_tokens=response.usage.cache_read_input_tokens,
382
+ ),
369
383
  )
370
384
  text_parts: list[str] = []
371
385
  thinking_parts: list[str] = []
372
- citations: list[Citation] = []
373
386
 
374
387
  for block in response.content:
375
388
  match block.type:
376
389
  case "tool_use":
377
- arguments = dict(block.input) if block.input else {}
378
390
  result.tool_calls.append(
379
391
  MCPToolCall(
380
392
  id=block.id,
381
393
  name=block.name,
382
- arguments=arguments,
394
+ arguments=dict(block.input) if block.input else {},
383
395
  _meta=mcp_types.RequestParams.Meta.model_validate(
384
396
  {"citations_enabled": citations_enabled},
385
397
  ),
@@ -387,9 +399,8 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
387
399
  )
388
400
  result.done = False
389
401
  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 []))
402
+ text_parts.append(block.text)
403
+ result.citations.extend(cls._citation(c) for c in (block.citations or []))
393
404
  case "thinking":
394
405
  if block.thinking:
395
406
  thinking_parts.append(block.thinking)
@@ -397,7 +408,6 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
397
408
  pass
398
409
 
399
410
  result.content = "".join(text_parts)
400
- result.citations = citations
401
411
  if thinking_parts:
402
412
  result.reasoning = "\n".join(thinking_parts)
403
413
  result.finish_reason = response.stop_reason
@@ -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"]
@@ -0,0 +1,219 @@
1
+ """ClaudeCLIAgent — runs ``claude`` CLI over SSH inside the env workspace.
2
+
3
+ SSH-execs the ``claude`` CLI on the remote workspace so all built-in tools
4
+ (Bash, Read, Write, Edit, Glob, Grep) operate on the env's filesystem.
5
+ MCP capabilities from the manifest are written as MCP server config so the
6
+ CLI can call env-hosted MCP tools too.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import logging
13
+ import shlex
14
+ from contextlib import AsyncExitStack
15
+ from typing import TYPE_CHECKING, Any, cast
16
+
17
+ import asyncssh
18
+
19
+ from hud.agents.base import Agent
20
+ from hud.agents.cli import (
21
+ PROCESS_BOUND_CREDENTIAL,
22
+ WINDOWS_SHELLS,
23
+ powershell,
24
+ powershell_quote,
25
+ resolve_executable,
26
+ run_jsonl,
27
+ )
28
+ from hud.agents.types import ClaudeCLIConfig
29
+ from hud.settings import settings
30
+ from hud.telemetry.context import get_trace_headers
31
+ from hud.utils.gateway import routes_to_gateway
32
+ from hud.utils.time import now_iso
33
+
34
+ from . import computer_mcp
35
+ from .events import ClaudeEvents
36
+
37
+ if TYPE_CHECKING:
38
+ from hud.capabilities import Connection, SSHClient
39
+ from hud.eval.run import Run
40
+
41
+ logger = logging.getLogger(__name__)
42
+
43
+ INPUT_PATH = ".hud_input.jsonl"
44
+ MCP_CONFIG_PATH = ".hud_mcp_config.json"
45
+ RUN_SCRIPT_PATH = ".hud_run.bat"
46
+
47
+ _MANAGED_CLAUDE_PATHS = {
48
+ "linux-x64": "/usr/local/lib/agents/claude/linux-x64/claude",
49
+ "linux-x64-musl": "/usr/local/lib/agents/claude/linux-x64-musl/claude",
50
+ }
51
+
52
+
53
+ class ClaudeCLIAgent(Agent[ClaudeCLIConfig]):
54
+ """Runs ``claude`` CLI over SSH inside the env workspace.
55
+
56
+ Stateless w.r.t. the env: driven by ``await agent(run)``. SSH is opened
57
+ live off the run. Environment MCP bindings are used directly; computer MCP
58
+ servers are bridged over the run's SSH connection.
59
+ """
60
+
61
+ config_cls = ClaudeCLIConfig
62
+
63
+ async def __call__(self, run: Run) -> None:
64
+ ssh = cast("SSHClient", await run.client.open("ssh"))
65
+ manifest = run.client.manifest
66
+ assert manifest is not None
67
+ shell = ssh.capability.params.get("shell", "bash")
68
+ windows = shell in WINDOWS_SHELLS
69
+ connection: Connection | None = run.connections.get("inference")
70
+ executable = await resolve_executable(
71
+ ssh, "claude", _MANAGED_CLAUDE_PATHS, run.runtime_config
72
+ )
73
+ rfb_count = sum(cap.protocol.partition("/")[0] == "rfb" for cap in manifest.bindings)
74
+
75
+ async with AsyncExitStack() as resources:
76
+ mcp_servers: dict[str, dict[str, Any]] = {}
77
+ for cap in manifest.bindings:
78
+ family = cap.protocol.partition("/")[0]
79
+ if family == "mcp":
80
+ name = cap.name
81
+ transport = "http" if cap.params["transport"] == "streamable-http" else "sse"
82
+ server: dict[str, Any] = {"type": transport, "url": cap.url}
83
+ if token := cap.params.get("auth_token"):
84
+ server["headers"] = {"Authorization": f"Bearer {token}"}
85
+ elif family == "rfb":
86
+ name = "computer-use" if rfb_count == 1 else f"computer-use-{cap.name}"
87
+ server = await resources.enter_async_context(
88
+ computer_mcp.bridge_computer_mcp(
89
+ ssh,
90
+ run.client.binding(cap.name),
91
+ self.config.screenshot_encoding,
92
+ shell=shell,
93
+ )
94
+ )
95
+ else:
96
+ continue
97
+ if name in mcp_servers:
98
+ raise RuntimeError(f"duplicate MCP server name {name!r}")
99
+ mcp_servers[name] = server
100
+
101
+ env: dict[str, str] = {
102
+ "ANTHROPIC_MODEL": self.config.model,
103
+ "ANTHROPIC_SMALL_FAST_MODEL": self.config.model,
104
+ "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
105
+ "DISABLE_AUTOUPDATER": "1",
106
+ "IS_SANDBOX": "1",
107
+ }
108
+ if connection is not None or routes_to_gateway(
109
+ "anthropic", gateway=self.config.gateway
110
+ ):
111
+ if connection is not None:
112
+ # The relay injects the scoped credential; the process never sees it.
113
+ # AUTH_TOKEN sends Authorization: Bearer, the header Codex uses too.
114
+ env["ANTHROPIC_BASE_URL"] = connection.client_url
115
+ env["ANTHROPIC_AUTH_TOKEN"] = PROCESS_BOUND_CREDENTIAL
116
+ elif settings.api_key:
117
+ env["ANTHROPIC_BASE_URL"] = settings.hud_gateway_url
118
+ env["ANTHROPIC_API_KEY"] = settings.api_key
119
+ else:
120
+ raise ValueError("HUD_API_KEY is required for HUD gateway routing")
121
+ env["CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS"] = "1"
122
+ env["DISABLE_AUTO_COMPACT"] = "1"
123
+ # Alias every model tier so background requests never bypass the gateway.
124
+ env["ANTHROPIC_DEFAULT_SONNET_MODEL"] = self.config.model
125
+ env["ANTHROPIC_DEFAULT_OPUS_MODEL"] = self.config.model
126
+ env["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = self.config.model
127
+ env["CLAUDE_CODE_SUBAGENT_MODEL"] = self.config.model
128
+ if connection is None and (trace_headers := get_trace_headers()):
129
+ env["ANTHROPIC_CUSTOM_HEADERS"] = "\n".join(
130
+ f"{name}: {value}" for name, value in trace_headers.items()
131
+ )
132
+ elif settings.anthropic_api_key:
133
+ env["ANTHROPIC_API_KEY"] = settings.anthropic_api_key
134
+
135
+ args = [
136
+ "--verbose",
137
+ "--input-format=stream-json",
138
+ "--output-format=stream-json",
139
+ "--print",
140
+ f"--permission-mode={self.config.permission_mode}",
141
+ ]
142
+ if self.config.max_steps > 0:
143
+ args.append(f"--max-turns={self.config.max_steps}")
144
+ if self.config.system_prompt:
145
+ args.extend(["--system-prompt", self.config.system_prompt])
146
+ for tool in self.config.allowed_tools:
147
+ args.extend(["--allowedTools", tool])
148
+ if mcp_servers:
149
+ args.extend(["--mcp-config", MCP_CONFIG_PATH])
150
+
151
+ if windows:
152
+ script = ";".join(
153
+ [
154
+ *(f"$env:{key}={powershell_quote(value)}" for key, value in env.items()),
155
+ f"Get-Content -Raw -Encoding UTF8 {powershell_quote(INPUT_PATH)}"
156
+ f" | & {powershell_quote(executable)} "
157
+ f"{' '.join(powershell_quote(arg) for arg in args)}",
158
+ "exit $LASTEXITCODE",
159
+ ]
160
+ )
161
+ command = powershell(script)
162
+ else:
163
+ env_prefix = " ".join(f"{key}={shlex.quote(value)}" for key, value in env.items())
164
+ cli = " ".join(shlex.quote(arg) for arg in [executable, *args])
165
+ invocation = f"{env_prefix} {cli}"
166
+ if connection is not None:
167
+ # A guarded connection is bound to the launched process, so exec the CLI itself.
168
+ invocation = f"exec env {invocation}"
169
+ command = f'export PATH="$HOME/.local/bin:$PATH"; {invocation}'
170
+
171
+ input_text = (
172
+ json.dumps(
173
+ {
174
+ "type": "user",
175
+ "message": {
176
+ "role": "user",
177
+ "content": [{"type": "text", "text": run.prompt_text}],
178
+ },
179
+ }
180
+ )
181
+ + "\n"
182
+ )
183
+ files: list[str] = []
184
+ try:
185
+ if mcp_servers:
186
+ await ssh.write_text(
187
+ MCP_CONFIG_PATH, json.dumps({"mcpServers": mcp_servers}, indent=2)
188
+ )
189
+ files.append(MCP_CONFIG_PATH)
190
+ if windows:
191
+ await ssh.write_text(INPUT_PATH, input_text)
192
+ await ssh.write_text(RUN_SCRIPT_PATH, f"@echo off\r\n{command}\r\n")
193
+ files += [INPUT_PATH, RUN_SCRIPT_PATH]
194
+ command = f"cmd /c {RUN_SCRIPT_PATH}"
195
+
196
+ logger.info("SSH exec claude CLI (%d chars)", len(command))
197
+ events = ClaudeEvents(run, started_at=now_iso())
198
+ returncode, stderr = await run_jsonl(
199
+ ssh,
200
+ command,
201
+ events.consume,
202
+ input_text=None if windows else input_text,
203
+ connections=(connection,) if connection is not None else (),
204
+ )
205
+ logger.info("exit=%s stderr=%d", returncode, len(stderr))
206
+ events.finish(returncode=returncode, stderr=stderr)
207
+ finally:
208
+ if files:
209
+ if windows:
210
+ cleanup = f"cmd /c del /f /q {' '.join(files)} 2>nul"
211
+ else:
212
+ cleanup = "rm -f -- " + " ".join(shlex.quote(path) for path in files)
213
+ try:
214
+ await ssh.run(cleanup, check=False)
215
+ except (OSError, asyncssh.Error):
216
+ logger.warning("Failed to remove Claude CLI runtime files")
217
+
218
+
219
+ __all__ = ["ClaudeCLIAgent"]