hud-python 0.5.18__tar.gz → 0.5.20__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 (337) hide show
  1. {hud_python-0.5.18 → hud_python-0.5.20}/PKG-INFO +6 -6
  2. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/base.py +72 -41
  3. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/claude.py +68 -33
  4. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/gemini.py +12 -10
  5. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/gemini_cua.py +14 -12
  6. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/openai.py +20 -20
  7. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/operator.py +25 -1
  8. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/tests/test_claude.py +217 -1
  9. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/__init__.py +3 -3
  10. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/build.py +16 -5
  11. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_build.py +2 -2
  12. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_build_module.py +4 -4
  13. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/interactive.py +10 -8
  14. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/validation.py +29 -21
  15. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/scenarios.py +1 -1
  16. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/tests/test_connection.py +1 -1
  17. {hud_python-0.5.18 → hud_python-0.5.20}/hud/patches/mcp_patches.py +2 -2
  18. {hud_python-0.5.18 → hud_python-0.5.20}/hud/shared/exceptions.py +7 -4
  19. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/base.py +24 -6
  20. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/bash.py +9 -21
  21. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/edit.py +0 -3
  22. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/session.py +10 -34
  23. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/utils.py +32 -0
  24. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/computer/anthropic.py +182 -38
  25. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/computer/tests/test_computer.py +39 -0
  26. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/executors/base.py +82 -1
  27. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/executors/pyautogui.py +31 -0
  28. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/executors/xdo.py +43 -0
  29. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/native_types.py +4 -3
  30. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_native_tool_e2e.py +26 -27
  31. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_native_types.py +150 -6
  32. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/tests/test_version.py +1 -1
  33. {hud_python-0.5.18 → hud_python-0.5.20}/hud/version.py +1 -1
  34. {hud_python-0.5.18 → hud_python-0.5.20}/pyproject.toml +5 -4
  35. {hud_python-0.5.18 → hud_python-0.5.20}/.gitignore +0 -0
  36. {hud_python-0.5.18 → hud_python-0.5.20}/LICENSE +0 -0
  37. {hud_python-0.5.18 → hud_python-0.5.20}/README.md +0 -0
  38. {hud_python-0.5.18 → hud_python-0.5.20}/examples/README.md +0 -0
  39. {hud_python-0.5.18 → hud_python-0.5.20}/hud/__init__.py +0 -0
  40. {hud_python-0.5.18 → hud_python-0.5.20}/hud/__main__.py +0 -0
  41. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/__init__.py +0 -0
  42. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/gateway.py +0 -0
  43. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/grounded_openai.py +0 -0
  44. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/misc/__init__.py +0 -0
  45. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/misc/integration_test_agent.py +0 -0
  46. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/misc/response_agent.py +0 -0
  47. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/openai_chat.py +0 -0
  48. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/resolver.py +0 -0
  49. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/tests/__init__.py +0 -0
  50. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/tests/conftest.py +0 -0
  51. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/tests/test_base.py +0 -0
  52. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/tests/test_base_runtime.py +0 -0
  53. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/tests/test_gemini.py +0 -0
  54. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/tests/test_grounded_openai_agent.py +0 -0
  55. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/tests/test_openai.py +0 -0
  56. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/tests/test_operator.py +0 -0
  57. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/tests/test_resolver.py +0 -0
  58. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/tests/test_run_eval.py +0 -0
  59. {hud_python-0.5.18 → hud_python-0.5.20}/hud/agents/types.py +0 -0
  60. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/__main__.py +0 -0
  61. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/analyze.py +0 -0
  62. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/clone.py +0 -0
  63. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/debug.py +0 -0
  64. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/deploy.py +0 -0
  65. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/dev.py +0 -0
  66. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/eval.py +0 -0
  67. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/flows/__init__.py +0 -0
  68. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/flows/dev.py +0 -0
  69. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/flows/init.py +0 -0
  70. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/flows/tasks.py +0 -0
  71. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/flows/templates.py +0 -0
  72. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/flows/tests/__init__.py +0 -0
  73. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/flows/tests/test_dev.py +0 -0
  74. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/get.py +0 -0
  75. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/init.py +0 -0
  76. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/link.py +0 -0
  77. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/list_func.py +0 -0
  78. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/pull.py +0 -0
  79. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/push.py +0 -0
  80. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/remove.py +0 -0
  81. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/rft.py +0 -0
  82. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/rft_status.py +0 -0
  83. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/__init__.py +0 -0
  84. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_analyze.py +0 -0
  85. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_analyze_metadata.py +0 -0
  86. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_analyze_module.py +0 -0
  87. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_build_failure.py +0 -0
  88. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_cli_init.py +0 -0
  89. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_cli_main.py +0 -0
  90. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_cli_more_wrappers.py +0 -0
  91. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_cli_root.py +0 -0
  92. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_clone.py +0 -0
  93. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_convert.py +0 -0
  94. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_cursor.py +0 -0
  95. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_debug.py +0 -0
  96. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_debug_directory_mode.py +0 -0
  97. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_deploy.py +0 -0
  98. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_dev.py +0 -0
  99. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_eval.py +0 -0
  100. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_eval_bedrock.py +0 -0
  101. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_init.py +0 -0
  102. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_list_func.py +0 -0
  103. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_main_module.py +0 -0
  104. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_mcp_server.py +0 -0
  105. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_pull.py +0 -0
  106. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_push.py +0 -0
  107. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_push_happy.py +0 -0
  108. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_push_wrapper.py +0 -0
  109. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_registry.py +0 -0
  110. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/tests/test_utils.py +0 -0
  111. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/__init__.py +0 -0
  112. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/build_display.py +0 -0
  113. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/build_logs.py +0 -0
  114. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/celebrate.py +0 -0
  115. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/config.py +0 -0
  116. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/context.py +0 -0
  117. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/cursor.py +0 -0
  118. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/docker.py +0 -0
  119. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/env_check.py +0 -0
  120. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/environment.py +0 -0
  121. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/git.py +0 -0
  122. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/local_runner.py +0 -0
  123. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/logging.py +0 -0
  124. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/mcp.py +0 -0
  125. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/metadata.py +0 -0
  126. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/package_runner.py +0 -0
  127. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/registry.py +0 -0
  128. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/remote_runner.py +0 -0
  129. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/runner.py +0 -0
  130. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/server.py +0 -0
  131. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/source_hash.py +0 -0
  132. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tasks.py +0 -0
  133. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/__init__.py +0 -0
  134. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_config.py +0 -0
  135. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_docker.py +0 -0
  136. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_docker_hints.py +0 -0
  137. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_env_check.py +0 -0
  138. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_environment.py +0 -0
  139. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_git.py +0 -0
  140. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_interactive_module.py +0 -0
  141. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_local_runner.py +0 -0
  142. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_logging_utils.py +0 -0
  143. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_metadata.py +0 -0
  144. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_package_runner.py +0 -0
  145. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_registry_utils.py +0 -0
  146. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_remote_runner.py +0 -0
  147. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_runner_modules.py +0 -0
  148. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_source_hash.py +0 -0
  149. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/tests/test_tasks.py +0 -0
  150. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/version_check.py +0 -0
  151. {hud_python-0.5.18 → hud_python-0.5.20}/hud/cli/utils/viewer.py +0 -0
  152. {hud_python-0.5.18 → hud_python-0.5.20}/hud/datasets/__init__.py +0 -0
  153. {hud_python-0.5.18 → hud_python-0.5.20}/hud/datasets/loader.py +0 -0
  154. {hud_python-0.5.18 → hud_python-0.5.20}/hud/datasets/runner.py +0 -0
  155. {hud_python-0.5.18 → hud_python-0.5.20}/hud/datasets/tests/__init__.py +0 -0
  156. {hud_python-0.5.18 → hud_python-0.5.20}/hud/datasets/tests/test_loader.py +0 -0
  157. {hud_python-0.5.18 → hud_python-0.5.20}/hud/datasets/tests/test_utils.py +0 -0
  158. {hud_python-0.5.18 → hud_python-0.5.20}/hud/datasets/utils.py +0 -0
  159. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/__init__.py +0 -0
  160. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/connection.py +0 -0
  161. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/connectors/__init__.py +0 -0
  162. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/connectors/base.py +0 -0
  163. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/connectors/local.py +0 -0
  164. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/connectors/mcp_config.py +0 -0
  165. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/connectors/openai.py +0 -0
  166. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/connectors/remote.py +0 -0
  167. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/environment.py +0 -0
  168. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/integrations/__init__.py +0 -0
  169. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/integrations/adk.py +0 -0
  170. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/integrations/anthropic.py +0 -0
  171. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/integrations/gemini.py +0 -0
  172. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/integrations/langchain.py +0 -0
  173. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/integrations/llamaindex.py +0 -0
  174. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/integrations/openai.py +0 -0
  175. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/mock.py +0 -0
  176. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/router.py +0 -0
  177. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/tests/__init__.py +0 -0
  178. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/tests/test_connectors.py +0 -0
  179. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/tests/test_environment.py +0 -0
  180. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/tests/test_integrations.py +0 -0
  181. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/tests/test_local_connectors.py +0 -0
  182. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/tests/test_scenarios.py +0 -0
  183. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/tests/test_tools.py +0 -0
  184. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/types.py +0 -0
  185. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/utils/__init__.py +0 -0
  186. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/utils/formats.py +0 -0
  187. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/utils/schema.py +0 -0
  188. {hud_python-0.5.18 → hud_python-0.5.20}/hud/environment/utils/tool_wrappers.py +0 -0
  189. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/__init__.py +0 -0
  190. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/context.py +0 -0
  191. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/display.py +0 -0
  192. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/instrument.py +0 -0
  193. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/manager.py +0 -0
  194. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/parallel.py +0 -0
  195. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/task.py +0 -0
  196. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/tests/__init__.py +0 -0
  197. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/tests/test_context.py +0 -0
  198. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/tests/test_eval.py +0 -0
  199. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/tests/test_manager.py +0 -0
  200. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/tests/test_parallel.py +0 -0
  201. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/tests/test_task.py +0 -0
  202. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/types.py +0 -0
  203. {hud_python-0.5.18 → hud_python-0.5.20}/hud/eval/utils.py +0 -0
  204. {hud_python-0.5.18 → hud_python-0.5.20}/hud/native/__init__.py +0 -0
  205. {hud_python-0.5.18 → hud_python-0.5.20}/hud/native/comparator.py +0 -0
  206. {hud_python-0.5.18 → hud_python-0.5.20}/hud/native/tests/__init__.py +0 -0
  207. {hud_python-0.5.18 → hud_python-0.5.20}/hud/native/tests/test_comparator.py +0 -0
  208. {hud_python-0.5.18 → hud_python-0.5.20}/hud/native/tests/test_native_init.py +0 -0
  209. {hud_python-0.5.18 → hud_python-0.5.20}/hud/patches/__init__.py +0 -0
  210. {hud_python-0.5.18 → hud_python-0.5.20}/hud/patches/warnings.py +0 -0
  211. {hud_python-0.5.18 → hud_python-0.5.20}/hud/py.typed +0 -0
  212. {hud_python-0.5.18 → hud_python-0.5.20}/hud/samples/__init__.py +0 -0
  213. {hud_python-0.5.18 → hud_python-0.5.20}/hud/samples/browser.py +0 -0
  214. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/__init__.py +0 -0
  215. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/context.py +0 -0
  216. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/helper/__init__.py +0 -0
  217. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/low_level.py +0 -0
  218. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/router.py +0 -0
  219. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/server.py +0 -0
  220. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/tests/__init__.py +0 -0
  221. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/tests/test_add_tool.py +0 -0
  222. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/tests/test_context.py +0 -0
  223. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/tests/test_mcp_server_handlers.py +0 -0
  224. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/tests/test_mcp_server_integration.py +0 -0
  225. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/tests/test_mcp_server_more.py +0 -0
  226. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/tests/test_run_wrapper.py +0 -0
  227. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/tests/test_server_extra.py +0 -0
  228. {hud_python-0.5.18 → hud_python-0.5.20}/hud/server/tests/test_sigterm_runner.py +0 -0
  229. {hud_python-0.5.18 → hud_python-0.5.20}/hud/settings.py +0 -0
  230. {hud_python-0.5.18 → hud_python-0.5.20}/hud/shared/__init__.py +0 -0
  231. {hud_python-0.5.18 → hud_python-0.5.20}/hud/shared/hints.py +0 -0
  232. {hud_python-0.5.18 → hud_python-0.5.20}/hud/shared/requests.py +0 -0
  233. {hud_python-0.5.18 → hud_python-0.5.20}/hud/shared/tests/__init__.py +0 -0
  234. {hud_python-0.5.18 → hud_python-0.5.20}/hud/shared/tests/test_exceptions.py +0 -0
  235. {hud_python-0.5.18 → hud_python-0.5.20}/hud/shared/tests/test_hints.py +0 -0
  236. {hud_python-0.5.18 → hud_python-0.5.20}/hud/shared/tests/test_requests.py +0 -0
  237. {hud_python-0.5.18 → hud_python-0.5.20}/hud/telemetry/__init__.py +0 -0
  238. {hud_python-0.5.18 → hud_python-0.5.20}/hud/telemetry/exporter.py +0 -0
  239. {hud_python-0.5.18 → hud_python-0.5.20}/hud/telemetry/instrument.py +0 -0
  240. {hud_python-0.5.18 → hud_python-0.5.20}/hud/telemetry/tests/__init__.py +0 -0
  241. {hud_python-0.5.18 → hud_python-0.5.20}/hud/telemetry/tests/test_eval_telemetry.py +0 -0
  242. {hud_python-0.5.18 → hud_python-0.5.20}/hud/telemetry/tests/test_exporter.py +0 -0
  243. {hud_python-0.5.18 → hud_python-0.5.20}/hud/telemetry/tests/test_instrument.py +0 -0
  244. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/__init__.py +0 -0
  245. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/agent.py +0 -0
  246. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/__init__.py +0 -0
  247. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/apply_patch.py +0 -0
  248. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/gemini_edit.py +0 -0
  249. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/gemini_shell.py +0 -0
  250. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/shell.py +0 -0
  251. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/tests/__init__.py +0 -0
  252. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/tests/test_apply_patch.py +0 -0
  253. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/tests/test_bash.py +0 -0
  254. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/tests/test_bash_extended.py +0 -0
  255. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/tests/test_edit.py +0 -0
  256. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/tests/test_gemini_tools.py +0 -0
  257. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/coding/tests/test_shell.py +0 -0
  258. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/computer/__init__.py +0 -0
  259. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/computer/gemini.py +0 -0
  260. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/computer/hud.py +0 -0
  261. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/computer/openai.py +0 -0
  262. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/computer/qwen.py +0 -0
  263. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/computer/settings.py +0 -0
  264. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/computer/tests/__init__.py +0 -0
  265. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/computer/tests/test_computer_actions.py +0 -0
  266. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/executors/__init__.py +0 -0
  267. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/executors/tests/__init__.py +0 -0
  268. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/executors/tests/test_base_executor.py +0 -0
  269. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/executors/tests/test_pyautogui_executor.py +0 -0
  270. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/__init__.py +0 -0
  271. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/base.py +0 -0
  272. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/gemini.py +0 -0
  273. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/glob.py +0 -0
  274. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/grep.py +0 -0
  275. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/list.py +0 -0
  276. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/read.py +0 -0
  277. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/tests/__init__.py +0 -0
  278. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/tests/test_glob.py +0 -0
  279. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/tests/test_grep.py +0 -0
  280. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/tests/test_list.py +0 -0
  281. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/filesystem/tests/test_read.py +0 -0
  282. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/grounding/__init__.py +0 -0
  283. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/grounding/config.py +0 -0
  284. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/grounding/grounded_tool.py +0 -0
  285. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/grounding/grounder.py +0 -0
  286. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/grounding/tests/__init__.py +0 -0
  287. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/grounding/tests/test_grounded_tool.py +0 -0
  288. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/hosted/__init__.py +0 -0
  289. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/hosted/base.py +0 -0
  290. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/hosted/code_execution.py +0 -0
  291. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/hosted/google_search.py +0 -0
  292. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/hosted/url_context.py +0 -0
  293. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/hosted/web_fetch.py +0 -0
  294. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/hosted/web_search.py +0 -0
  295. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/jupyter.py +0 -0
  296. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/memory/__init__.py +0 -0
  297. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/memory/base.py +0 -0
  298. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/memory/claude.py +0 -0
  299. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/memory/gemini.py +0 -0
  300. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/memory/session.py +0 -0
  301. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/memory/tests/__init__.py +0 -0
  302. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/memory/tests/test_claude.py +0 -0
  303. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/memory/tests/test_gemini.py +0 -0
  304. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/memory/tests/test_session.py +0 -0
  305. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/playwright.py +0 -0
  306. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/response.py +0 -0
  307. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/submit.py +0 -0
  308. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/__init__.py +0 -0
  309. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_agent_tool.py +0 -0
  310. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_base.py +0 -0
  311. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_init.py +0 -0
  312. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_jupyter_tool.py +0 -0
  313. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_playwright_tool.py +0 -0
  314. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_response.py +0 -0
  315. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_submit.py +0 -0
  316. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_tools.py +0 -0
  317. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_tools_init.py +0 -0
  318. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_types.py +0 -0
  319. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/tests/test_utils.py +0 -0
  320. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/types.py +0 -0
  321. {hud_python-0.5.18 → hud_python-0.5.20}/hud/tools/utils.py +0 -0
  322. {hud_python-0.5.18 → hud_python-0.5.20}/hud/types.py +0 -0
  323. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/__init__.py +0 -0
  324. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/env.py +0 -0
  325. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/hud_console.py +0 -0
  326. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/mcp.py +0 -0
  327. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/pretty_errors.py +0 -0
  328. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/strict_schema.py +0 -0
  329. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/telemetry.py +0 -0
  330. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/tests/__init__.py +0 -0
  331. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/tests/test_init.py +0 -0
  332. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/tests/test_mcp.py +0 -0
  333. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/tests/test_pretty_errors.py +0 -0
  334. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/tests/test_telemetry.py +0 -0
  335. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/tests/test_tool_shorthand.py +0 -0
  336. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/tool_shorthand.py +0 -0
  337. {hud_python-0.5.18 → hud_python-0.5.20}/hud/utils/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hud-python
3
- Version: 0.5.18
3
+ Version: 0.5.20
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
@@ -51,7 +51,7 @@ Requires-Dist: toml>=0.10.2
51
51
  Requires-Dist: typer>=0.9.0
52
52
  Requires-Dist: watchfiles>=0.21.0
53
53
  Provides-Extra: agent
54
- Requires-Dist: anthropic>=0.75; extra == 'agent'
54
+ Requires-Dist: anthropic>=0.78.0; extra == 'agent'
55
55
  Requires-Dist: datasets>=2.14.0; extra == 'agent'
56
56
  Requires-Dist: google-genai; extra == 'agent'
57
57
  Requires-Dist: langchain>=1.1.0; extra == 'agent'
@@ -60,7 +60,7 @@ Requires-Dist: openai-agents; extra == 'agent'
60
60
  Requires-Dist: pillow>=11.1.0; extra == 'agent'
61
61
  Requires-Dist: tornado>=6.5.2; extra == 'agent'
62
62
  Provides-Extra: agents
63
- Requires-Dist: anthropic>=0.75; extra == 'agents'
63
+ Requires-Dist: anthropic>=0.78.0; extra == 'agents'
64
64
  Requires-Dist: datasets>=2.14.0; extra == 'agents'
65
65
  Requires-Dist: google-genai; extra == 'agents'
66
66
  Requires-Dist: langchain>=1.1.0; extra == 'agents'
@@ -69,9 +69,9 @@ Requires-Dist: openai-agents; extra == 'agents'
69
69
  Requires-Dist: pillow>=11.1.0; extra == 'agents'
70
70
  Requires-Dist: tornado>=6.5.2; extra == 'agents'
71
71
  Provides-Extra: bedrock
72
- Requires-Dist: anthropic[bedrock]>=0.75; extra == 'bedrock'
72
+ Requires-Dist: anthropic[bedrock]>=0.78.0; extra == 'bedrock'
73
73
  Provides-Extra: dev
74
- Requires-Dist: anthropic>=0.75; extra == 'dev'
74
+ Requires-Dist: anthropic>=0.78.0; extra == 'dev'
75
75
  Requires-Dist: datasets>=2.14.0; extra == 'dev'
76
76
  Requires-Dist: dotenv>=0.9.9; extra == 'dev'
77
77
  Requires-Dist: google-adk; extra == 'dev'
@@ -92,7 +92,7 @@ Requires-Dist: pytest-asyncio; extra == 'dev'
92
92
  Requires-Dist: pytest-cov; extra == 'dev'
93
93
  Requires-Dist: pytest-mock; extra == 'dev'
94
94
  Requires-Dist: pytest>=8.1.1; extra == 'dev'
95
- Requires-Dist: ruff>=0.11.8; extra == 'dev'
95
+ Requires-Dist: ruff<0.15.0,>=0.11.8; extra == 'dev'
96
96
  Requires-Dist: tornado>=6.5.2; extra == 'dev'
97
97
  Description-Content-Type: text/markdown
98
98
 
@@ -88,6 +88,9 @@ class MCPAgent(ABC):
88
88
  Returns a NativeToolSpec that can be used to register the tool with
89
89
  the provider's native API format.
90
90
 
91
+ When the spec data is a list (model-specific variants), specs are tried in order
92
+ and the first one whose supports_model() matches wins.
93
+
91
94
  Falls back to legacy name-based detection for backwards compatibility with
92
95
  old environments that don't emit native_tools metadata.
93
96
 
@@ -101,42 +104,28 @@ class MCPAgent(ABC):
101
104
  generic function calling.
102
105
  """
103
106
  spec: NativeToolSpec | None = None
107
+ spec_data = None
104
108
 
105
109
  # First try metadata-based resolution
106
110
  if tool.meta:
107
111
  native_tools = tool.meta.get("native_tools", {})
108
- spec_dict = native_tools.get(self.agent_type().value)
109
-
110
- if spec_dict and isinstance(spec_dict, dict):
111
- # Extract known fields and put the rest in extra
112
- known_fields = {
113
- "api_type",
114
- "api_name",
115
- "beta",
116
- "hosted",
117
- "role",
118
- "supported_models",
119
- }
120
- extra = {k: v for k, v in spec_dict.items() if k not in known_fields}
121
-
122
- # Convert supported_models list to tuple for frozen model
123
- supported_models_raw = spec_dict.get("supported_models")
124
- supported_models: tuple[str, ...] | None = None
125
- if supported_models_raw:
126
- supported_models = tuple(supported_models_raw)
127
-
128
- spec = NativeToolSpec(
129
- api_type=spec_dict.get("api_type"),
130
- api_name=spec_dict.get("api_name"),
131
- beta=spec_dict.get("beta"),
132
- hosted=spec_dict.get("hosted", False),
133
- role=spec_dict.get("role"),
134
- supported_models=supported_models,
135
- extra=extra,
136
- )
112
+ spec_data = native_tools.get(self.agent_type().value)
113
+
114
+ if isinstance(spec_data, list):
115
+ # List of specs -- pick first model-matching spec
116
+ for item in spec_data:
117
+ if not isinstance(item, dict):
118
+ continue
119
+ candidate = _parse_spec_dict(item)
120
+ if candidate and candidate.supports_model(self.model):
121
+ spec = candidate
122
+ break
123
+ elif isinstance(spec_data, dict):
124
+ spec = _parse_spec_dict(spec_data)
137
125
 
138
126
  # Fall back to legacy name-based detection for old environments
139
- if spec is None:
127
+ # Only if metadata didn't contain specs for this agent type at all
128
+ if spec is None and not spec_data:
140
129
  spec = self._legacy_native_spec_fallback(tool)
141
130
 
142
131
  # Check if current model supports this native spec
@@ -170,23 +159,33 @@ class MCPAgent(ABC):
170
159
  The role is used for mutual exclusion - when an agent accepts a tool
171
160
  natively, other tools with the same role are excluded.
172
161
 
162
+ Checks metadata first, then falls back to legacy name-based detection
163
+ so old environments without native_tools metadata still get proper
164
+ role-based exclusion.
165
+
173
166
  Args:
174
167
  tool: MCP Tool object to check
175
168
 
176
169
  Returns:
177
170
  The role string if any native spec defines one, None otherwise
178
171
  """
179
- if not tool.meta:
180
- return None
181
-
182
- native_tools = tool.meta.get("native_tools", {})
183
- if not native_tools:
184
- return None
185
-
186
- # Check all specs for a role (they should all have the same role)
187
- for spec_dict in native_tools.values():
188
- if isinstance(spec_dict, dict) and spec_dict.get("role"):
189
- return spec_dict["role"]
172
+ # Check metadata-based specs first
173
+ if tool.meta:
174
+ native_tools = tool.meta.get("native_tools", {})
175
+ if native_tools:
176
+ # Check all specs for a role (they should all have the same role)
177
+ for spec_data in native_tools.values():
178
+ if isinstance(spec_data, dict) and spec_data.get("role"):
179
+ return spec_data["role"]
180
+ if isinstance(spec_data, list):
181
+ for item in spec_data:
182
+ if isinstance(item, dict) and item.get("role"):
183
+ return item["role"]
184
+
185
+ # Fall back to legacy detection for old environments without metadata
186
+ legacy_spec = self._legacy_native_spec_fallback(tool)
187
+ if legacy_spec and legacy_spec.role:
188
+ return legacy_spec.role
190
189
 
191
190
  return None
192
191
 
@@ -229,6 +228,10 @@ class MCPAgent(ABC):
229
228
  else:
230
229
  result.native.append((tool, spec))
231
230
 
231
+ # Collect api_names claimed by native tools to prevent name collisions
232
+ claimed_api_names = {s.api_name for _, s in result.native if s.api_name}
233
+ claimed_api_names |= {s.api_name for _, s in result.hosted if s.api_name}
234
+
232
235
  # Second pass: process tools without native specs (generic function tools)
233
236
  for tool in tools:
234
237
  spec = self.resolve_native_spec(tool)
@@ -242,6 +245,13 @@ class MCPAgent(ABC):
242
245
  result.skipped.append((tool, f"role '{tool_role}' already claimed by native tool"))
243
246
  continue
244
247
 
248
+ # Check if this tool's name collides with a native tool's api_name
249
+ if tool.name in claimed_api_names:
250
+ result.skipped.append(
251
+ (tool, f"name '{tool.name}' collides with native tool api_name")
252
+ )
253
+ continue
254
+
245
255
  result.generic.append(tool)
246
256
 
247
257
  return result
@@ -723,6 +733,27 @@ class MCPAgent(ABC):
723
733
  self.ctx = None
724
734
 
725
735
 
736
+ def _parse_spec_dict(spec_dict: dict[str, Any]) -> NativeToolSpec | None:
737
+ """Parse a dict (from MCP meta) into a NativeToolSpec."""
738
+ if not spec_dict:
739
+ return None
740
+ known_fields = {"api_type", "api_name", "beta", "hosted", "role", "supported_models"}
741
+ extra = {k: v for k, v in spec_dict.items() if k not in known_fields}
742
+ supported_models_raw = spec_dict.get("supported_models")
743
+ supported_models: tuple[str, ...] | None = None
744
+ if supported_models_raw:
745
+ supported_models = tuple(supported_models_raw)
746
+ return NativeToolSpec(
747
+ api_type=spec_dict.get("api_type"),
748
+ api_name=spec_dict.get("api_name"),
749
+ beta=spec_dict.get("beta"),
750
+ hosted=spec_dict.get("hosted", False),
751
+ role=spec_dict.get("role"),
752
+ supported_models=supported_models,
753
+ extra=extra,
754
+ )
755
+
756
+
726
757
  def _format_error_result(error_message: str) -> MCPToolResult:
727
758
  return MCPToolResult(content=text_to_blocks(error_message), isError=True)
728
759
 
@@ -20,6 +20,7 @@ from anthropic.types.beta import (
20
20
  BetaTextBlockParam,
21
21
  BetaToolBash20250124Param,
22
22
  BetaToolComputerUse20250124Param,
23
+ BetaToolComputerUse20251124Param,
23
24
  BetaToolParam,
24
25
  BetaToolResultBlockParam,
25
26
  BetaToolTextEditor20250728Param,
@@ -71,41 +72,49 @@ class ClaudeAgent(MCPAgent):
71
72
 
72
73
  Supports old environments that expose tools like 'anthropic_computer',
73
74
  'bash', or 'str_replace_based_edit_tool' without native_tools metadata.
75
+
76
+ Each tuple is ordered by preference — first name that exists wins.
77
+ Only returns a spec if this tool IS that preferred match.
74
78
  """
75
- name = tool.name
79
+ import fnmatch
80
+
81
+ available = {t.name for t in (self._available_tools or [])} | {tool.name}
82
+ preferred = lambda names: next((n for n in names if n in available), None) == tool.name
76
83
 
77
- # Check for computer tool patterns
78
- for pattern in self._LEGACY_COMPUTER_NAMES:
79
- if name == pattern or name.endswith(f"_{pattern}"):
80
- logger.debug("Legacy fallback: detected %s as computer tool", name)
84
+ if preferred(self._LEGACY_COMPUTER_NAMES):
85
+ logger.debug("Legacy fallback: detected %s as computer tool", tool.name)
86
+ model_lower = (self.model or "").lower()
87
+ if any(
88
+ fnmatch.fnmatch(model_lower, p) for p in ("claude-opus-4-5*", "claude-opus-4-6*")
89
+ ):
81
90
  return NativeToolSpec(
82
- api_type="computer_20250124",
91
+ api_type="computer_20251124",
83
92
  api_name="computer",
84
- beta="computer-use-2025-01-24",
93
+ beta="computer-use-2025-11-24",
85
94
  role="computer",
86
95
  )
96
+ return NativeToolSpec(
97
+ api_type="computer_20250124",
98
+ api_name="computer",
99
+ beta="computer-use-2025-01-24",
100
+ role="computer",
101
+ )
87
102
 
88
- # Check for bash tool patterns
89
- for pattern in self._LEGACY_BASH_NAMES:
90
- if name == pattern or name.endswith(f"_{pattern}"):
91
- logger.debug("Legacy fallback: detected %s as bash tool", name)
92
- return NativeToolSpec(
93
- api_type="bash_20250124",
94
- api_name="bash",
95
- beta="computer-use-2025-01-24",
96
- role="shell",
97
- )
103
+ if preferred(self._LEGACY_BASH_NAMES):
104
+ logger.debug("Legacy fallback: detected %s as bash tool", tool.name)
105
+ return NativeToolSpec(
106
+ api_type="bash_20250124",
107
+ api_name="bash",
108
+ role="shell",
109
+ )
98
110
 
99
- # Check for text editor tool patterns
100
- for pattern in self._LEGACY_EDITOR_NAMES:
101
- if name == pattern or name.endswith(f"_{pattern}"):
102
- logger.debug("Legacy fallback: detected %s as text_editor tool", name)
103
- return NativeToolSpec(
104
- api_type="text_editor_20250728",
105
- api_name="str_replace_based_edit_tool",
106
- beta="computer-use-2025-01-24",
107
- role="editor",
108
- )
111
+ if preferred(self._LEGACY_EDITOR_NAMES):
112
+ logger.debug("Legacy fallback: detected %s as text_editor tool", tool.name)
113
+ return NativeToolSpec(
114
+ api_type="text_editor_20250728",
115
+ api_name="str_replace_based_edit_tool",
116
+ role="editor",
117
+ )
109
118
 
110
119
  return None
111
120
 
@@ -142,7 +151,7 @@ class ClaudeAgent(MCPAgent):
142
151
  self.has_computer_tool = False
143
152
  self.tool_mapping: dict[str, str] = {}
144
153
  self.claude_tools: list[BetaToolUnionParam] = []
145
- self._required_betas: set[str] = {"fine-grained-tool-streaming-2025-05-14"}
154
+ self._required_betas: set[str] = set()
146
155
 
147
156
  def _on_tools_ready(self) -> None:
148
157
  """Build Claude-specific tool mappings after tools are discovered."""
@@ -191,7 +200,9 @@ class ClaudeAgent(MCPAgent):
191
200
  messages_cached = self._add_prompt_caching(messages)
192
201
 
193
202
  # betas to use - collected during tool conversion based on native specs
194
- betas = list(self._required_betas)
203
+ # Only pass betas when non-empty; an empty list can produce an empty
204
+ # anthropic-beta header which the API rejects.
205
+ betas: list[str] | Omit = list(self._required_betas) if self._required_betas else Omit()
195
206
 
196
207
  # Bedrock doesn't support .stream() - use create(stream=True) instead
197
208
  if isinstance(self.anthropic_client, AsyncAnthropicBedrock):
@@ -331,7 +342,7 @@ class ClaudeAgent(MCPAgent):
331
342
  self.has_computer_tool = False
332
343
  self.tool_mapping: dict[str, str] = {}
333
344
  self.claude_tools: list[BetaToolUnionParam] = []
334
- self._required_betas: set[str] = {"fine-grained-tool-streaming-2025-05-14"}
345
+ self._required_betas: set[str] = set()
335
346
 
336
347
  categorized = self.categorize_tools()
337
348
 
@@ -371,6 +382,7 @@ class ClaudeAgent(MCPAgent):
371
382
  name=tool.name,
372
383
  description=tool.description,
373
384
  input_schema=tool.inputSchema,
385
+ eager_input_streaming=True,
374
386
  )
375
387
  self.tool_mapping[tool.name] = tool.name
376
388
  self.claude_tools.append(claude_tool)
@@ -387,7 +399,7 @@ class ClaudeAgent(MCPAgent):
387
399
  Claude's native tools have fixed names that must be used exactly.
388
400
  """
389
401
  match spec.api_type:
390
- case "computer_20250124":
402
+ case "computer_20250124" | "computer_20251124":
391
403
  return "computer"
392
404
  case "bash_20250124":
393
405
  return "bash"
@@ -407,8 +419,32 @@ class ClaudeAgent(MCPAgent):
407
419
  Claude-specific tool parameter
408
420
  """
409
421
  match spec.api_type:
422
+ case "computer_20251124":
423
+ display_width = spec.extra.get("display_width")
424
+ display_height = spec.extra.get("display_height")
425
+
426
+ if display_width is None or display_height is None:
427
+ import warnings
428
+
429
+ warnings.warn(
430
+ "Computer tool missing display dimensions in native_specs.extra. "
431
+ "Falling back to computer_settings. This fallback will be removed "
432
+ "in v0.6.0. Update your tool to pass display_width/display_height.",
433
+ DeprecationWarning,
434
+ stacklevel=2,
435
+ )
436
+ display_width = display_width or computer_settings.ANTHROPIC_COMPUTER_WIDTH
437
+ display_height = display_height or computer_settings.ANTHROPIC_COMPUTER_HEIGHT
438
+
439
+ return BetaToolComputerUse20251124Param(
440
+ type="computer_20251124",
441
+ name="computer",
442
+ display_number=1,
443
+ display_width_px=display_width,
444
+ display_height_px=display_height,
445
+ enable_zoom=True,
446
+ )
410
447
  case "computer_20250124":
411
- # Get display dimensions from spec.extra, fallback to settings
412
448
  display_width = spec.extra.get("display_width")
413
449
  display_height = spec.extra.get("display_height")
414
450
 
@@ -425,7 +461,6 @@ class ClaudeAgent(MCPAgent):
425
461
  display_width = display_width or computer_settings.ANTHROPIC_COMPUTER_WIDTH
426
462
  display_height = display_height or computer_settings.ANTHROPIC_COMPUTER_HEIGHT
427
463
 
428
- # Claude expects name to be literal "computer"
429
464
  return BetaToolComputerUse20250124Param(
430
465
  type="computer_20250124",
431
466
  name="computer",
@@ -47,20 +47,22 @@ class GeminiAgent(MCPAgent):
47
47
 
48
48
  Supports old environments that expose tools like 'gemini_computer'
49
49
  without native_tools metadata.
50
+
51
+ Each tuple is ordered by preference — first name that exists wins.
52
+ Only returns a spec if this tool IS that preferred match.
50
53
  """
51
54
  from hud.tools.native_types import NativeToolSpec
52
55
 
53
- name = tool.name
56
+ available = {t.name for t in (self._available_tools or [])} | {tool.name}
57
+ preferred = lambda names: next((n for n in names if n in available), None) == tool.name
54
58
 
55
- # Check for computer tool patterns
56
- for pattern in self._LEGACY_COMPUTER_NAMES:
57
- if name == pattern or name.endswith(f"_{pattern}"):
58
- logger.debug("Legacy fallback: detected %s as computer tool", name)
59
- return NativeToolSpec(
60
- api_type="computer_use",
61
- api_name="gemini_computer",
62
- role="computer",
63
- )
59
+ if preferred(self._LEGACY_COMPUTER_NAMES):
60
+ logger.debug("Legacy fallback: detected %s as computer tool", tool.name)
61
+ return NativeToolSpec(
62
+ api_type="computer_use",
63
+ api_name="gemini_computer",
64
+ role="computer",
65
+ )
64
66
 
65
67
  return None
66
68
 
@@ -78,25 +78,27 @@ class GeminiCUAAgent(GeminiAgent):
78
78
  return AgentType.GEMINI_CUA
79
79
 
80
80
  # Legacy tool name patterns for backwards compatibility
81
- _LEGACY_COMPUTER_NAMES = ("gemini_computer", "computer_gemini", "computer")
81
+ _LEGACY_COMPUTER_NAMES = ("gemini_computer", "computer")
82
82
 
83
83
  def _legacy_native_spec_fallback(self, tool: types.Tool) -> NativeToolSpec | None:
84
84
  """Detect Gemini CUA native tools by name for backwards compatibility.
85
85
 
86
86
  Supports old environments that expose tools like 'gemini_computer'
87
87
  without native_tools metadata.
88
+
89
+ Each tuple is ordered by preference — first name that exists wins.
90
+ Only returns a spec if this tool IS that preferred match.
88
91
  """
89
- name = tool.name
90
-
91
- # Check for computer tool patterns
92
- for pattern in self._LEGACY_COMPUTER_NAMES:
93
- if name == pattern or name.endswith(f"_{pattern}"):
94
- logger.debug("Legacy fallback: detected %s as computer_use tool", name)
95
- return NativeToolSpec(
96
- api_type="computer_use",
97
- api_name="gemini_computer",
98
- role="computer",
99
- )
92
+ available = {t.name for t in (self._available_tools or [])} | {tool.name}
93
+ preferred = lambda names: next((n for n in names if n in available), None) == tool.name
94
+
95
+ if preferred(self._LEGACY_COMPUTER_NAMES):
96
+ logger.debug("Legacy fallback: detected %s as computer_use tool", tool.name)
97
+ return NativeToolSpec(
98
+ api_type="computer_use",
99
+ api_name="gemini_computer",
100
+ role="computer",
101
+ )
100
102
 
101
103
  return None
102
104
 
@@ -62,28 +62,28 @@ class OpenAIAgent(MCPAgent):
62
62
 
63
63
  Supports old environments that expose tools like 'shell' or 'apply_patch'
64
64
  without native_tools metadata.
65
+
66
+ Each tuple is ordered by preference — first name that exists wins.
67
+ Only returns a spec if this tool IS that preferred match.
65
68
  """
66
- name = tool.name
67
-
68
- # Check for shell tool patterns
69
- for pattern in self._LEGACY_SHELL_NAMES:
70
- if name == pattern or name.endswith(f"_{pattern}"):
71
- logger.debug("Legacy fallback: detected %s as shell tool", name)
72
- return NativeToolSpec(
73
- api_type="shell",
74
- api_name="shell",
75
- role="shell",
76
- )
69
+ available = {t.name for t in (self._available_tools or [])} | {tool.name}
70
+ preferred = lambda names: next((n for n in names if n in available), None) == tool.name
71
+
72
+ if preferred(self._LEGACY_SHELL_NAMES):
73
+ logger.debug("Legacy fallback: detected %s as shell tool", tool.name)
74
+ return NativeToolSpec(
75
+ api_type="shell",
76
+ api_name="shell",
77
+ role="shell",
78
+ )
77
79
 
78
- # Check for apply_patch tool patterns
79
- for pattern in self._LEGACY_APPLY_PATCH_NAMES:
80
- if name == pattern or name.endswith(f"_{pattern}"):
81
- logger.debug("Legacy fallback: detected %s as apply_patch tool", name)
82
- return NativeToolSpec(
83
- api_type="apply_patch",
84
- api_name="apply_patch",
85
- role="editor",
86
- )
80
+ if preferred(self._LEGACY_APPLY_PATCH_NAMES):
81
+ logger.debug("Legacy fallback: detected %s as apply_patch tool", tool.name)
82
+ return NativeToolSpec(
83
+ api_type="apply_patch",
84
+ api_name="apply_patch",
85
+ role="editor",
86
+ )
87
87
 
88
88
  return None
89
89
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import logging
5
6
  from typing import TYPE_CHECKING, Any, ClassVar, Literal
6
7
 
7
8
  import mcp.types as types
@@ -17,7 +18,7 @@ from openai.types.responses.response_input_param import (
17
18
  from openai.types.shared_params.reasoning import Reasoning
18
19
 
19
20
  from hud.tools.computer.settings import computer_settings
20
- from hud.tools.native_types import NativeToolSpec # noqa: TC001
21
+ from hud.tools.native_types import NativeToolSpec
21
22
  from hud.types import AgentType, BaseAgentConfig, MCPToolCall, MCPToolResult
22
23
  from hud.utils.types import with_signature
23
24
 
@@ -25,6 +26,8 @@ from .base import MCPAgent
25
26
  from .openai import OpenAIAgent
26
27
  from .types import OperatorConfig, OperatorCreateParams
27
28
 
29
+ logger = logging.getLogger(__name__)
30
+
28
31
  if TYPE_CHECKING:
29
32
  from openai.types.responses.response_computer_tool_call import PendingSafetyCheck
30
33
 
@@ -201,3 +204,24 @@ class OperatorAgent(OpenAIAgent):
201
204
  if isinstance(content, types.TextContent) and result.isError:
202
205
  self.console.error_log(f"Computer tool error: {content.text}")
203
206
  return None
207
+
208
+ _LEGACY_COMPUTER_NAMES = ("openai_computer",)
209
+
210
+ def _legacy_native_spec_fallback(self, tool: types.Tool) -> NativeToolSpec | None:
211
+ """Detect Operator native tools by name for backwards compatibility.
212
+
213
+ Each tuple is ordered by preference — first name that exists wins.
214
+ Only returns a spec if this tool IS that preferred match.
215
+ """
216
+ available = {t.name for t in (self._available_tools or [])} | {tool.name}
217
+ preferred = lambda names: next((n for n in names if n in available), None) == tool.name
218
+
219
+ if preferred(self._LEGACY_COMPUTER_NAMES):
220
+ logger.debug("Legacy fallback: detected %s as computer tool", tool.name)
221
+ return NativeToolSpec(
222
+ api_type="computer_use_preview",
223
+ api_name="computer",
224
+ role="computer",
225
+ )
226
+
227
+ return super()._legacy_native_spec_fallback(tool)