hud 0.6.16.dev2__tar.gz → 0.6.16.dev3__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 (311) hide show
  1. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/PKG-INFO +2 -1
  2. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/agent.py +82 -9
  3. hud-0.6.16.dev3/hud/agents/claude/tools/base.py +26 -0
  4. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/tools/coding.py +89 -9
  5. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/tools/computer.py +102 -4
  6. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/tools/tests/test_computer.py +18 -4
  7. hud-0.6.16.dev3/hud/agents/tests/test_claude_agent.py +261 -0
  8. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tests/test_provider_native_tools.py +19 -0
  9. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/eval.py +2 -1
  10. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/jobs.py +2 -1
  11. hud-0.6.16.dev3/hud/cli/qa.py +218 -0
  12. hud-0.6.16.dev3/hud/cli/tests/test_jobs.py +38 -0
  13. hud-0.6.16.dev3/hud/cli/tests/test_qa.py +190 -0
  14. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/trace.py +2 -2
  15. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/egress.py +10 -8
  16. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/namespace.py +3 -1
  17. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/robot/gym.py +1 -1
  18. hud-0.6.16.dev3/hud/environment/tests/test_platform.py +52 -0
  19. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/test_robot_regressions.py +52 -0
  20. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/test_workspace.py +3 -1
  21. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/job.py +2 -2
  22. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/run.py +8 -7
  23. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/runtime/__init__.py +0 -2
  24. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/runtime/core.py +12 -26
  25. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/runtime/daytona.py +22 -0
  26. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/runtime/docker.py +50 -19
  27. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/runtime/local.py +60 -25
  28. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/runtime/modal.py +52 -21
  29. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/tests/test_docker_provider.py +185 -7
  30. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/tests/test_job.py +19 -0
  31. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/tests/test_local_runtime.py +71 -1
  32. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/tests/test_rollout.py +17 -16
  33. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/telemetry/robot/recorder.py +11 -3
  34. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/platform.py +12 -0
  35. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/process.py +77 -1
  36. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/requests.py +9 -2
  37. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/tests/test_platform.py +18 -2
  38. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/tests/test_process.py +39 -1
  39. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/tests/test_requests.py +16 -0
  40. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/version.py +1 -1
  41. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/pyproject.toml +1 -0
  42. hud-0.6.16.dev2/hud/agents/claude/tools/base.py +0 -17
  43. hud-0.6.16.dev2/hud/agents/tests/test_claude_agent.py +0 -144
  44. hud-0.6.16.dev2/hud/cli/qa.py +0 -193
  45. hud-0.6.16.dev2/hud/cli/tests/test_qa.py +0 -171
  46. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/.gitignore +0 -0
  47. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/LICENSE +0 -0
  48. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/README.md +0 -0
  49. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/__init__.py +0 -0
  50. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/__main__.py +0 -0
  51. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/__init__.py +0 -0
  52. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/base.py +0 -0
  53. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/browser_use/__init__.py +0 -0
  54. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/browser_use/agent.py +0 -0
  55. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/__init__.py +0 -0
  56. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/sdk/__init__.py +0 -0
  57. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/sdk/agent.py +0 -0
  58. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/sdk/computer_mcp.py +0 -0
  59. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/tools/__init__.py +0 -0
  60. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/tools/hosted.py +0 -0
  61. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/tools/mcp_proxy.py +0 -0
  62. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/tools/settings.py +0 -0
  63. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/claude/tools/tests/__init__.py +0 -0
  64. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/__init__.py +0 -0
  65. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/agent.py +0 -0
  66. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/settings.py +0 -0
  67. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/tools/__init__.py +0 -0
  68. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/tools/base.py +0 -0
  69. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/tools/coding.py +0 -0
  70. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/tools/computer.py +0 -0
  71. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/tools/filesystem.py +0 -0
  72. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/tools/hosted.py +0 -0
  73. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/tools/mcp_proxy.py +0 -0
  74. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/tools/tests/__init__.py +0 -0
  75. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/gemini/tools/tests/test_computer.py +0 -0
  76. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/misc/__init__.py +0 -0
  77. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/misc/response_automation.py +0 -0
  78. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/__init__.py +0 -0
  79. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/agent.py +0 -0
  80. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/tools/__init__.py +0 -0
  81. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/tools/apply_patch.py +0 -0
  82. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/tools/base.py +0 -0
  83. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/tools/coding.py +0 -0
  84. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/tools/computer.py +0 -0
  85. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/tools/hosted.py +0 -0
  86. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/tools/mcp_proxy.py +0 -0
  87. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/tools/strict_schema.py +0 -0
  88. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/tools/tests/__init__.py +0 -0
  89. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/tools/tests/test_computer.py +0 -0
  90. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai/tools/tests/test_strict_schema.py +0 -0
  91. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai_compatible/__init__.py +0 -0
  92. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai_compatible/agent.py +0 -0
  93. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai_compatible/tools/__init__.py +0 -0
  94. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai_compatible/tools/base.py +0 -0
  95. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai_compatible/tools/filesystem.py +0 -0
  96. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/openai_compatible/tools/mcp_proxy.py +0 -0
  97. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/robot/__init__.py +0 -0
  98. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/robot/adapter.py +0 -0
  99. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/robot/agent.py +0 -0
  100. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/robot/batching.py +0 -0
  101. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/robot/dataset.py +0 -0
  102. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/robot/model.py +0 -0
  103. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tests/__init__.py +0 -0
  104. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tests/test_apply_patch.py +0 -0
  105. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tests/test_base.py +0 -0
  106. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tests/test_claude_sdk_agent.py +0 -0
  107. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tests/test_gemini_agent.py +0 -0
  108. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tests/test_openai_agent.py +0 -0
  109. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tests/test_openai_compatible_agent.py +0 -0
  110. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tests/test_robot_dataset_writer.py +0 -0
  111. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tests/test_tool_agent.py +0 -0
  112. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tests/test_trace.py +0 -0
  113. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tool_agent.py +0 -0
  114. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tools/__init__.py +0 -0
  115. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tools/base.py +0 -0
  116. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tools/hosted.py +0 -0
  117. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tools/mcp.py +0 -0
  118. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tools/rfb.py +0 -0
  119. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/tools/ssh.py +0 -0
  120. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/agents/types.py +0 -0
  121. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/capabilities/__init__.py +0 -0
  122. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/capabilities/base.py +0 -0
  123. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/capabilities/cdp.py +0 -0
  124. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/capabilities/mcp.py +0 -0
  125. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/capabilities/rfb.py +0 -0
  126. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/capabilities/robot.py +0 -0
  127. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/capabilities/ssh.py +0 -0
  128. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/capabilities/tests/__init__.py +0 -0
  129. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/capabilities/tests/test_rfb.py +0 -0
  130. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/capabilities/tests/test_ssh.py +0 -0
  131. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/__init__.py +0 -0
  132. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/__main__.py +0 -0
  133. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/cancel.py +0 -0
  134. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/client.py +0 -0
  135. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/deploy.py +0 -0
  136. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/init.py +0 -0
  137. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/login.py +0 -0
  138. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/models.py +0 -0
  139. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/presets.py +0 -0
  140. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/serve.py +0 -0
  141. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/sync.py +0 -0
  142. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/task.py +0 -0
  143. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/templates.py +0 -0
  144. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/__init__.py +0 -0
  145. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/test_cli_init.py +0 -0
  146. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/test_cli_main.py +0 -0
  147. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/test_cli_more_wrappers.py +0 -0
  148. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/test_deploy.py +0 -0
  149. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/test_eval_bedrock.py +0 -0
  150. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/test_eval_config.py +0 -0
  151. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/test_init.py +0 -0
  152. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/test_main_module.py +0 -0
  153. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/test_sync_export.py +0 -0
  154. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/test_trace.py +0 -0
  155. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/tests/test_usage.py +0 -0
  156. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/__init__.py +0 -0
  157. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/api.py +0 -0
  158. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/build_display.py +0 -0
  159. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/build_logs.py +0 -0
  160. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/config.py +0 -0
  161. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/context.py +0 -0
  162. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/display.py +0 -0
  163. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/jobs.py +0 -0
  164. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/registry.py +0 -0
  165. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/source.py +0 -0
  166. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/tasks.py +0 -0
  167. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/tests/__init__.py +0 -0
  168. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/tests/test_build_display.py +0 -0
  169. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/tests/test_config.py +0 -0
  170. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/tests/test_context.py +0 -0
  171. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/tests/test_registry.py +0 -0
  172. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/tests/test_source.py +0 -0
  173. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/tests/test_tasks.py +0 -0
  174. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/tests/test_version_check.py +0 -0
  175. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/usage.py +0 -0
  176. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/cli/utils/version_check.py +0 -0
  177. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/clients/__init__.py +0 -0
  178. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/clients/client.py +0 -0
  179. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/clients/tests/__init__.py +0 -0
  180. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/clients/tests/test_connect.py +0 -0
  181. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/conftest.py +0 -0
  182. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/__init__.py +0 -0
  183. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/arguments.py +0 -0
  184. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/env.py +0 -0
  185. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/file_tracker.py +0 -0
  186. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/robot/__init__.py +0 -0
  187. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/robot/bridge.py +0 -0
  188. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/robot/endpoint.py +0 -0
  189. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/server.py +0 -0
  190. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/__init__.py +0 -0
  191. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/conftest.py +0 -0
  192. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/test_arguments.py +0 -0
  193. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/test_capability_backing.py +0 -0
  194. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/test_file_tracker.py +0 -0
  195. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/test_file_tracking.py +0 -0
  196. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/test_loader.py +0 -0
  197. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/test_manifest.py +0 -0
  198. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/test_server.py +0 -0
  199. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/test_sessions.py +0 -0
  200. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/tests/test_tunnel.py +0 -0
  201. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/utils.py +0 -0
  202. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/environment/workspace.py +0 -0
  203. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/__init__.py +0 -0
  204. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/chat.py +0 -0
  205. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/docker-seccomp.json +0 -0
  206. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/file_tracking.py +0 -0
  207. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/runtime/compose.py +0 -0
  208. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/runtime/hosted.py +0 -0
  209. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/runtime/hud.py +0 -0
  210. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/sync.py +0 -0
  211. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/task.py +0 -0
  212. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/taskset.py +0 -0
  213. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/tests/__init__.py +0 -0
  214. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/tests/test_chat.py +0 -0
  215. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/tests/test_file_tracking_observer.py +0 -0
  216. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/tests/test_hosted.py +0 -0
  217. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/tests/test_shared.py +0 -0
  218. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/tests/test_sync.py +0 -0
  219. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/eval/tests/test_task.py +0 -0
  220. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/graders/__init__.py +0 -0
  221. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/graders/base.py +0 -0
  222. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/graders/bash.py +0 -0
  223. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/graders/combine.py +0 -0
  224. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/graders/judge.py +0 -0
  225. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/graders/results.py +0 -0
  226. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/graders/text.py +0 -0
  227. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/__init__.py +0 -0
  228. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/Dockerfile +0 -0
  229. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/__init__.py +0 -0
  230. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/adapt.py +0 -0
  231. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/env.py +0 -0
  232. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/export.py +0 -0
  233. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/install.sh +0 -0
  234. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/__init__.py +0 -0
  235. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/conftest.py +0 -0
  236. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/agent-lifecycle/environment/Dockerfile +0 -0
  237. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/agent-lifecycle/instruction.md +0 -0
  238. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/agent-lifecycle/solution/solve.sh +0 -0
  239. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/agent-lifecycle/task.toml +0 -0
  240. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/agent-lifecycle/tests/test.sh +0 -0
  241. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/Dockerfile +0 -0
  242. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/docker-compose.yaml +0 -0
  243. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/Dockerfile +0 -0
  244. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/__init__.py +0 -0
  245. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/hello-mcp/environment/mcp-server/server.py +0 -0
  246. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/hello-mcp/instruction.md +0 -0
  247. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/hello-mcp/solution/solve.sh +0 -0
  248. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/hello-mcp/task.toml +0 -0
  249. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/hello-mcp/tests/test.sh +0 -0
  250. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/phase-boundary/environment/Dockerfile +0 -0
  251. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/phase-boundary/environment/entrypoint.sh +0 -0
  252. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/phase-boundary/instruction.md +0 -0
  253. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/phase-boundary/solution/solve.sh +0 -0
  254. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/phase-boundary/task.toml +0 -0
  255. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/phase-boundary/tests/test.sh +0 -0
  256. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/sidecar-reachability/environment/Dockerfile +0 -0
  257. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/sidecar-reachability/environment/docker-compose.yaml +0 -0
  258. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/sidecar-reachability/instruction.md +0 -0
  259. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/sidecar-reachability/solution/solve.sh +0 -0
  260. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/sidecar-reachability/task.toml +0 -0
  261. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/sidecar-reachability/tests/Dockerfile +0 -0
  262. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/sidecar-reachability/tests/test.sh +0 -0
  263. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/environment/Dockerfile +0 -0
  264. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/environment/entrypoint.sh +0 -0
  265. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/instruction.md +0 -0
  266. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/solution/solve.sh +0 -0
  267. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/task.toml +0 -0
  268. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/tasks/verifier-lifecycle/tests/test.sh +0 -0
  269. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/test_contract.py +0 -0
  270. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/test_harbor.py +0 -0
  271. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/integrations/harbor/tests/test_integration.py +0 -0
  272. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/patches/__init__.py +0 -0
  273. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/patches/mcp_patches.py +0 -0
  274. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/patches/tests/__init__.py +0 -0
  275. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/patches/tests/test_warnings.py +0 -0
  276. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/patches/warnings.py +0 -0
  277. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/py.typed +0 -0
  278. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/settings.py +0 -0
  279. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/telemetry/__init__.py +0 -0
  280. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/telemetry/context.py +0 -0
  281. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/telemetry/exporter.py +0 -0
  282. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/telemetry/instrument.py +0 -0
  283. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/telemetry/robot/__init__.py +0 -0
  284. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/telemetry/robot/video.py +0 -0
  285. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/telemetry/span.py +0 -0
  286. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/telemetry/tests/__init__.py +0 -0
  287. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/telemetry/tests/test_exporter.py +0 -0
  288. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/telemetry/tests/test_instrument.py +0 -0
  289. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/train/__init__.py +0 -0
  290. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/train/base.py +0 -0
  291. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/train/client.py +0 -0
  292. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/train/tests/__init__.py +0 -0
  293. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/train/tests/test_client.py +0 -0
  294. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/train/types.py +0 -0
  295. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/types.py +0 -0
  296. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/__init__.py +0 -0
  297. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/docker.py +0 -0
  298. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/exceptions.py +0 -0
  299. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/gateway.py +0 -0
  300. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/hints.py +0 -0
  301. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/hud_console.py +0 -0
  302. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/modules.py +0 -0
  303. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/naming.py +0 -0
  304. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/serialization.py +0 -0
  305. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/tests/__init__.py +0 -0
  306. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/tests/test_exceptions.py +0 -0
  307. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/tests/test_gateway.py +0 -0
  308. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/tests/test_hints.py +0 -0
  309. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/tests/test_hud_console.py +0 -0
  310. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/tests/test_serialization.py +0 -0
  311. {hud-0.6.16.dev2 → hud-0.6.16.dev3}/hud/utils/time.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: hud
3
- Version: 0.6.16.dev2
3
+ Version: 0.6.16.dev3
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
@@ -39,6 +39,7 @@ Requires-Dist: asyncssh>=2.23.0
39
39
  Requires-Dist: asyncvnc>=1.3.0
40
40
  Requires-Dist: fastmcp==3.2.0
41
41
  Requires-Dist: google-genai
42
+ Requires-Dist: httpx2<3,>=2.0.0
42
43
  Requires-Dist: httpx<1,>=0.23.0
43
44
  Requires-Dist: mcp<2.0,>=1.24.0
44
45
  Requires-Dist: openai>=2.45.0
@@ -2,13 +2,23 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import asyncio
5
6
  import copy
6
7
  import json
7
8
  import logging
8
9
  from typing import TYPE_CHECKING, Literal, cast
9
10
 
11
+ import httpx
12
+ import httpx2
10
13
  import mcp.types as mcp_types
11
- from anthropic import AsyncAnthropic, AsyncAnthropicBedrock, Omit
14
+ from anthropic import (
15
+ APIConnectionError,
16
+ APIStatusError,
17
+ APITimeoutError,
18
+ AsyncAnthropic,
19
+ AsyncAnthropicBedrock,
20
+ Omit,
21
+ )
12
22
  from anthropic.types import CacheControlEphemeralParam
13
23
  from anthropic.types.beta import (
14
24
  BetaBase64ImageSourceParam,
@@ -42,6 +52,19 @@ logger = logging.getLogger(__name__)
42
52
  ClaudeImageMediaType = Literal["image/jpeg", "image/png", "image/gif", "image/webp"]
43
53
  ClaudeToolResultContent = BetaTextBlockParam | BetaImageBlockParam | BetaRequestDocumentBlockParam
44
54
 
55
+ _RETRYABLE_STREAM_ERROR_TYPES = frozenset(
56
+ {
57
+ "api_error",
58
+ "gateway_error",
59
+ "gateway_timeout",
60
+ "overloaded_error",
61
+ "rate_limit_error",
62
+ "timeout_error",
63
+ "upstream_error",
64
+ }
65
+ )
66
+ _STREAM_RETRY_DELAY_SECONDS = 1.0
67
+
45
68
 
46
69
  class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
47
70
  """Anthropic Claude agent. Drives SSH (coding), RFB (computer), and MCP capabilities."""
@@ -168,6 +191,60 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
168
191
 
169
192
  # ─── Anthropic call ───────────────────────────────────────────────
170
193
 
194
+ async def _stream_response(
195
+ self,
196
+ client: AsyncAnthropic,
197
+ *,
198
+ messages: list[BetaMessageParam],
199
+ system: str | Omit,
200
+ tools: list[BetaToolUnionParam],
201
+ tool_choice: BetaToolChoiceAutoParam,
202
+ betas: list[str] | Omit,
203
+ ) -> BetaMessage:
204
+ retries = 0
205
+ while True:
206
+ stream_opened = False
207
+ try:
208
+ async with client.beta.messages.stream(
209
+ model=self.config.model,
210
+ system=system,
211
+ max_tokens=self.config.max_tokens,
212
+ messages=messages,
213
+ tools=tools,
214
+ tool_choice=tool_choice,
215
+ betas=betas,
216
+ ) as stream:
217
+ stream_opened = True
218
+ async for _ in stream:
219
+ pass
220
+ return await stream.get_final_message()
221
+ except (
222
+ APIConnectionError,
223
+ APIStatusError,
224
+ APITimeoutError,
225
+ httpx.TransportError,
226
+ httpx2.TransportError,
227
+ ) as exc:
228
+ inline_error = (
229
+ isinstance(exc, APIStatusError)
230
+ and exc.status_code == 200
231
+ and exc.type in _RETRYABLE_STREAM_ERROR_TYPES
232
+ )
233
+ interrupted_stream = stream_opened and isinstance(
234
+ exc,
235
+ APIConnectionError
236
+ | APITimeoutError
237
+ | httpx.TransportError
238
+ | httpx2.TransportError,
239
+ )
240
+ if retries or not (inline_error or interrupted_stream):
241
+ raise
242
+
243
+ retries += 1
244
+ error_type = exc.type if isinstance(exc, APIStatusError) else type(exc).__name__
245
+ logger.warning("Claude stream failed with %s; retrying once", error_type)
246
+ await asyncio.sleep(_STREAM_RETRY_DELAY_SECONDS)
247
+
171
248
  async def get_response(
172
249
  self,
173
250
  state: RunState[BetaMessageParam],
@@ -202,18 +279,14 @@ class ClaudeAgent(ToolAgent[BetaMessageParam, ClaudeConfig]):
202
279
  )
203
280
  else:
204
281
  client = cast("AsyncAnthropic", self.anthropic_client)
205
- async with client.beta.messages.stream(
206
- model=self.config.model,
207
- system=system,
208
- max_tokens=self.config.max_tokens,
282
+ response = await self._stream_response(
283
+ client,
209
284
  messages=messages_cached,
285
+ system=system,
210
286
  tools=tools,
211
287
  tool_choice=tool_choice,
212
288
  betas=betas,
213
- ) as stream:
214
- async for _ in stream:
215
- pass
216
- response = await stream.get_final_message()
289
+ )
217
290
 
218
291
  state.messages.append(
219
292
  BetaMessageParam(role="assistant", content=response.content),
@@ -0,0 +1,26 @@
1
+ """Claude-specific tool spec."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from hud.agents.tools.base import AgentToolSpec
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class ClaudeToolSpec(AgentToolSpec):
12
+ """Claude tool spec — adds the optional Anthropic beta flag."""
13
+
14
+ beta: str | None = None
15
+
16
+
17
+ def is_anthropic_model(model: str | None) -> bool:
18
+ """Whether the model accepts Anthropic server-tool shorthands (``bash_20250124`` etc.).
19
+
20
+ Non-Anthropic models served over Anthropic-compatible endpoints (e.g. OpenRouter)
21
+ reject those shorthands, so tools fall back to plain ``input_schema`` definitions.
22
+ """
23
+ return bool(model) and "claude" in model.lower()
24
+
25
+
26
+ __all__ = ["ClaudeToolSpec", "is_anthropic_model"]
@@ -10,12 +10,13 @@ from hud.agents.tools import SSHTool
10
10
  from hud.agents.tools.base import result_text, tool_err
11
11
  from hud.types import MCPToolResult
12
12
 
13
- from .base import ClaudeToolSpec
13
+ from .base import ClaudeToolSpec, is_anthropic_model
14
14
 
15
15
  if TYPE_CHECKING:
16
16
  from anthropic.types.beta import (
17
17
  BetaToolBash20250124Param,
18
18
  BetaToolTextEditor20250728Param,
19
+ BetaToolUnionParam,
19
20
  )
20
21
 
21
22
 
@@ -24,11 +25,64 @@ CLAUDE_BASH_SPEC = ClaudeToolSpec(
24
25
  api_name="bash",
25
26
  )
26
27
 
28
+ GENERIC_BASH_SPEC = ClaudeToolSpec(
29
+ api_type="function",
30
+ api_name="bash",
31
+ )
32
+
27
33
  CLAUDE_TEXT_EDITOR_SPEC = ClaudeToolSpec(
28
34
  api_type="text_editor_20250728",
29
35
  api_name="str_replace_based_edit_tool",
30
36
  )
31
37
 
38
+ GENERIC_TEXT_EDITOR_SPEC = ClaudeToolSpec(
39
+ api_type="function",
40
+ api_name="str_replace_based_edit_tool",
41
+ )
42
+
43
+ _BASH_INPUT_SCHEMA: dict[str, Any] = {
44
+ "type": "object",
45
+ "properties": {
46
+ "command": {
47
+ "type": "string",
48
+ "description": "The bash command to run.",
49
+ },
50
+ },
51
+ "required": ["command"],
52
+ }
53
+
54
+ _TEXT_EDITOR_INPUT_SCHEMA: dict[str, Any] = {
55
+ "type": "object",
56
+ "properties": {
57
+ "command": {
58
+ "type": "string",
59
+ "enum": ["view", "create", "str_replace", "insert"],
60
+ "description": "The editor operation to run.",
61
+ },
62
+ "path": {
63
+ "type": "string",
64
+ "description": "Absolute path to the file.",
65
+ },
66
+ "file_text": {
67
+ "type": "string",
68
+ "description": "Full file contents for `create`.",
69
+ },
70
+ "old_str": {
71
+ "type": "string",
72
+ "description": "Exact existing text to replace for `str_replace`; must be unique.",
73
+ },
74
+ "new_str": {
75
+ "type": "string",
76
+ "description": "Replacement text for `str_replace`, or text to insert for `insert`.",
77
+ },
78
+ "insert_line": {
79
+ "type": "integer",
80
+ "description": "Line number after which to insert for `insert` (0 = top of file).",
81
+ },
82
+ },
83
+ "required": ["command", "path"],
84
+ }
85
+
32
86
 
33
87
  class ClaudeBashTool(SSHTool):
34
88
  """Claude's native ``bash_20250124`` schema, executed over SSH."""
@@ -37,10 +91,18 @@ class ClaudeBashTool(SSHTool):
37
91
 
38
92
  @classmethod
39
93
  def default_spec(cls, model: str) -> ClaudeToolSpec:
40
- del model
41
- return CLAUDE_BASH_SPEC
42
-
43
- def to_params(self) -> BetaToolBash20250124Param:
94
+ return CLAUDE_BASH_SPEC if is_anthropic_model(model) else GENERIC_BASH_SPEC
95
+
96
+ def to_params(self) -> BetaToolUnionParam:
97
+ if self.spec.api_type == "function":
98
+ return cast(
99
+ "BetaToolUnionParam",
100
+ {
101
+ "name": self.name,
102
+ "description": "Run a command in a bash shell on the remote machine.",
103
+ "input_schema": _BASH_INPUT_SCHEMA,
104
+ },
105
+ )
44
106
  return cast(
45
107
  "BetaToolBash20250124Param",
46
108
  {"type": self.spec.api_type, "name": self.name},
@@ -77,14 +139,25 @@ class ClaudeTextEditorTool(SSHTool):
77
139
 
78
140
  @classmethod
79
141
  def default_spec(cls, model: str) -> ClaudeToolSpec:
80
- del model
81
- return CLAUDE_TEXT_EDITOR_SPEC
142
+ return CLAUDE_TEXT_EDITOR_SPEC if is_anthropic_model(model) else GENERIC_TEXT_EDITOR_SPEC
82
143
 
83
144
  @property
84
145
  def provider_name(self) -> str:
85
146
  return self.spec.api_name
86
147
 
87
- def to_params(self) -> BetaToolTextEditor20250728Param:
148
+ def to_params(self) -> BetaToolUnionParam:
149
+ if self.spec.api_type == "function":
150
+ return cast(
151
+ "BetaToolUnionParam",
152
+ {
153
+ "name": self.provider_name,
154
+ "description": (
155
+ "View, create, and edit files on the remote machine "
156
+ "(view/create/str_replace/insert)."
157
+ ),
158
+ "input_schema": _TEXT_EDITOR_INPUT_SCHEMA,
159
+ },
160
+ )
88
161
  return cast(
89
162
  "BetaToolTextEditor20250728Param",
90
163
  {"type": self.spec.api_type, "name": self.provider_name},
@@ -142,4 +215,11 @@ class ClaudeTextEditorTool(SSHTool):
142
215
  return await self.file_write(path, "".join(lines))
143
216
 
144
217
 
145
- __all__ = ["CLAUDE_BASH_SPEC", "CLAUDE_TEXT_EDITOR_SPEC", "ClaudeBashTool", "ClaudeTextEditorTool"]
218
+ __all__ = [
219
+ "CLAUDE_BASH_SPEC",
220
+ "CLAUDE_TEXT_EDITOR_SPEC",
221
+ "GENERIC_BASH_SPEC",
222
+ "GENERIC_TEXT_EDITOR_SPEC",
223
+ "ClaudeBashTool",
224
+ "ClaudeTextEditorTool",
225
+ ]
@@ -19,12 +19,13 @@ from hud.agents.tools.base import tool_err, tool_ok
19
19
  from hud.capabilities.rfb import ScreenshotEncoding, WebPScreenshotEncoding
20
20
  from hud.types import MCPToolResult
21
21
 
22
- from .base import ClaudeToolSpec
22
+ from .base import ClaudeToolSpec, is_anthropic_model
23
23
 
24
24
  if TYPE_CHECKING:
25
25
  from anthropic.types.beta import (
26
26
  BetaToolComputerUse20250124Param,
27
27
  BetaToolComputerUse20251124Param,
28
+ BetaToolUnionParam,
28
29
  )
29
30
 
30
31
  from hud.agents.tools.rfb import Button
@@ -110,9 +111,89 @@ CLAUDE_COMPUTER_SPECS: tuple[ClaudeToolSpec, ...] = (
110
111
  ),
111
112
  )
112
113
 
113
- # Fallback for unknown models — use the latest version.
114
+ # Fallback for unknown Claude models — use the latest version.
114
115
  _DEFAULT_COMPUTER_SPEC = CLAUDE_COMPUTER_SPECS[0]
115
116
 
117
+ GENERIC_COMPUTER_SPEC = ClaudeToolSpec(
118
+ api_type="function",
119
+ api_name="computer",
120
+ )
121
+
122
+ _COMPUTER_INPUT_SCHEMA: dict[str, Any] = {
123
+ "type": "object",
124
+ "properties": {
125
+ "action": {
126
+ "type": "string",
127
+ "enum": [
128
+ "screenshot",
129
+ "zoom",
130
+ "left_click",
131
+ "right_click",
132
+ "middle_click",
133
+ "double_click",
134
+ "triple_click",
135
+ "mouse_move",
136
+ "left_mouse_down",
137
+ "left_mouse_up",
138
+ "type",
139
+ "key",
140
+ "hold_key",
141
+ "scroll",
142
+ "left_click_drag",
143
+ "wait",
144
+ "cursor_position",
145
+ ],
146
+ "description": "The action to perform on the screen.",
147
+ },
148
+ "coordinate": {
149
+ "type": "array",
150
+ "items": {"type": "integer"},
151
+ "minItems": 2,
152
+ "maxItems": 2,
153
+ "description": "[x, y] pixel coordinate for click/move/scroll, or drag end point.",
154
+ },
155
+ "start_coordinate": {
156
+ "type": "array",
157
+ "items": {"type": "integer"},
158
+ "minItems": 2,
159
+ "maxItems": 2,
160
+ "description": "[x, y] drag start point for `left_click_drag`.",
161
+ },
162
+ "text": {
163
+ "type": "string",
164
+ "description": (
165
+ "Text to type for `type`; key chord (xdotool syntax, e.g. `ctrl+s`) for "
166
+ "`key`/`hold_key`; optional modifier keys held during clicks and scrolls."
167
+ ),
168
+ },
169
+ "duration": {
170
+ "type": "number",
171
+ "description": "Seconds to hold for `hold_key` or pause for `wait`.",
172
+ },
173
+ "scroll_direction": {
174
+ "type": "string",
175
+ "enum": ["up", "down", "left", "right"],
176
+ "description": "Direction for `scroll`.",
177
+ },
178
+ "scroll_amount": {
179
+ "type": "integer",
180
+ "description": "Number of scroll clicks for `scroll`.",
181
+ },
182
+ "repeat": {
183
+ "type": "integer",
184
+ "description": "Times to repeat the key chord for `key`.",
185
+ },
186
+ "region": {
187
+ "type": "array",
188
+ "items": {"type": "integer"},
189
+ "minItems": 4,
190
+ "maxItems": 4,
191
+ "description": "[x0, y0, x1, y1] region to magnify for `zoom`.",
192
+ },
193
+ },
194
+ "required": ["action"],
195
+ }
196
+
116
197
 
117
198
  class ClaudeComputerTool(RFBTool):
118
199
  """Claude's native ``computer_use`` schema, executed over an RFB capability."""
@@ -121,12 +202,29 @@ class ClaudeComputerTool(RFBTool):
121
202
 
122
203
  @classmethod
123
204
  def default_spec(cls, model: str) -> ClaudeToolSpec | None:
205
+ if not is_anthropic_model(model):
206
+ return GENERIC_COMPUTER_SPEC
124
207
  for candidate in CLAUDE_COMPUTER_SPECS:
125
208
  if candidate.supports_model(model):
126
209
  return candidate
127
210
  return _DEFAULT_COMPUTER_SPEC
128
211
 
129
- def to_params(self) -> BetaToolComputerUse20250124Param | BetaToolComputerUse20251124Param:
212
+ def to_params(
213
+ self,
214
+ ) -> BetaToolComputerUse20250124Param | BetaToolComputerUse20251124Param | BetaToolUnionParam:
215
+ if self.spec.api_type == "function":
216
+ return cast(
217
+ "BetaToolUnionParam",
218
+ {
219
+ "name": self.name,
220
+ "description": (
221
+ "Control the remote computer's screen, mouse, and keyboard. "
222
+ f"The display is {self.display_width}x{self.display_height} pixels. "
223
+ "Most actions return a screenshot of the result."
224
+ ),
225
+ "input_schema": _COMPUTER_INPUT_SCHEMA,
226
+ },
227
+ )
130
228
  if self.spec.api_type == "computer_20251124":
131
229
  return cast(
132
230
  "BetaToolComputerUse20251124Param",
@@ -375,4 +473,4 @@ def _crop_png(
375
473
  return buf.getvalue(), encoding.mime_type
376
474
 
377
475
 
378
- __all__ = ["CLAUDE_COMPUTER_SPECS", "ClaudeComputerTool"]
476
+ __all__ = ["CLAUDE_COMPUTER_SPECS", "GENERIC_COMPUTER_SPEC", "ClaudeComputerTool"]
@@ -14,6 +14,7 @@ from PIL import Image
14
14
 
15
15
  from hud.agents.claude.tools.computer import (
16
16
  CLAUDE_COMPUTER_SPECS,
17
+ GENERIC_COMPUTER_SPEC,
17
18
  ClaudeComputerTool,
18
19
  _crop_png,
19
20
  _hold_keys,
@@ -120,18 +121,31 @@ def test_default_spec_per_model() -> None:
120
121
  spec_45 = ClaudeComputerTool.default_spec("claude-sonnet-4-5-20250101")
121
122
  assert spec_45 is not None
122
123
  assert spec_45.api_type == "computer_20250124"
123
- # Unknown model falls back to the latest spec.
124
- spec_unknown = ClaudeComputerTool.default_spec("totally-unknown")
124
+ # Unknown Claude model falls back to the latest spec.
125
+ spec_unknown = ClaudeComputerTool.default_spec("claude-totally-unknown")
125
126
  assert spec_unknown is not None
126
127
  assert spec_unknown.api_type == "computer_20251124"
128
+ # Non-Anthropic models get the generic input_schema spec.
129
+ assert ClaudeComputerTool.default_spec("qwen/qwen3.8-max") is GENERIC_COMPUTER_SPEC
127
130
 
128
131
 
129
132
  def test_to_params_reflects_spec_version() -> None:
130
133
  tool = RecordingComputer()
131
134
  tool.spec = CLAUDE_COMPUTER_SPECS[0]
132
- assert tool.to_params()["type"] == "computer_20251124"
135
+ assert tool.to_params().get("type") == "computer_20251124"
133
136
  tool.spec = CLAUDE_COMPUTER_SPECS[1]
134
- assert tool.to_params()["type"] == "computer_20250124"
137
+ assert tool.to_params().get("type") == "computer_20250124"
138
+
139
+
140
+ def test_to_params_generic_spec_uses_input_schema() -> None:
141
+ tool = RecordingComputer()
142
+ tool.spec = GENERIC_COMPUTER_SPEC
143
+ params: Any = tool.to_params()
144
+ assert "type" not in params
145
+ assert params["name"] == "computer"
146
+ assert "200x100" in params["description"]
147
+ assert params["input_schema"]["required"] == ["action"]
148
+ assert "left_click" in params["input_schema"]["properties"]["action"]["enum"]
135
149
 
136
150
 
137
151
  # ─── action dispatch ──────────────────────────────────────────────────