hud-python 0.5.3__tar.gz → 0.5.5__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 (305) hide show
  1. {hud_python-0.5.3 → hud_python-0.5.5}/PKG-INFO +1 -1
  2. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/claude.py +12 -4
  3. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/gemini.py +12 -4
  4. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/openai.py +12 -4
  5. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_openai.py +2 -1
  6. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/environment.py +17 -5
  7. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/scenarios.py +68 -54
  8. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/hud_console.py +30 -17
  9. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_version.py +1 -1
  10. {hud_python-0.5.3 → hud_python-0.5.5}/hud/version.py +1 -1
  11. {hud_python-0.5.3 → hud_python-0.5.5}/pyproject.toml +1 -1
  12. {hud_python-0.5.3 → hud_python-0.5.5}/.gitignore +0 -0
  13. {hud_python-0.5.3 → hud_python-0.5.5}/LICENSE +0 -0
  14. {hud_python-0.5.3 → hud_python-0.5.5}/README.md +0 -0
  15. {hud_python-0.5.3 → hud_python-0.5.5}/examples/README.md +0 -0
  16. {hud_python-0.5.3 → hud_python-0.5.5}/hud/__init__.py +0 -0
  17. {hud_python-0.5.3 → hud_python-0.5.5}/hud/__main__.py +0 -0
  18. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/__init__.py +0 -0
  19. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/base.py +0 -0
  20. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/gateway.py +0 -0
  21. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/gemini_cua.py +0 -0
  22. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/grounded_openai.py +0 -0
  23. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/misc/__init__.py +0 -0
  24. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/misc/integration_test_agent.py +0 -0
  25. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/misc/response_agent.py +0 -0
  26. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/openai_chat.py +0 -0
  27. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/operator.py +0 -0
  28. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/resolver.py +0 -0
  29. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/__init__.py +0 -0
  30. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/conftest.py +0 -0
  31. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_base.py +0 -0
  32. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_base_runtime.py +0 -0
  33. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_claude.py +0 -0
  34. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_client.py +0 -0
  35. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_gemini.py +0 -0
  36. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_grounded_openai_agent.py +0 -0
  37. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_operator.py +0 -0
  38. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_resolver.py +0 -0
  39. {hud_python-0.5.3 → hud_python-0.5.5}/hud/agents/tests/test_run_eval.py +0 -0
  40. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/__init__.py +0 -0
  41. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/__main__.py +0 -0
  42. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/analyze.py +0 -0
  43. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/build.py +0 -0
  44. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/clone.py +0 -0
  45. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/debug.py +0 -0
  46. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/dev.py +0 -0
  47. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/eval.py +0 -0
  48. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/__init__.py +0 -0
  49. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/dev.py +0 -0
  50. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/init.py +0 -0
  51. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/tasks.py +0 -0
  52. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/templates.py +0 -0
  53. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/tests/__init__.py +0 -0
  54. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/flows/tests/test_dev.py +0 -0
  55. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/get.py +0 -0
  56. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/init.py +0 -0
  57. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/list_func.py +0 -0
  58. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/pull.py +0 -0
  59. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/push.py +0 -0
  60. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/remove.py +0 -0
  61. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/rft.py +0 -0
  62. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/rft_status.py +0 -0
  63. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/__init__.py +0 -0
  64. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_analyze.py +0 -0
  65. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_analyze_metadata.py +0 -0
  66. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_analyze_module.py +0 -0
  67. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_build.py +0 -0
  68. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_build_failure.py +0 -0
  69. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_build_module.py +0 -0
  70. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_cli_init.py +0 -0
  71. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_cli_main.py +0 -0
  72. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_cli_more_wrappers.py +0 -0
  73. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_cli_root.py +0 -0
  74. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_clone.py +0 -0
  75. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_convert.py +0 -0
  76. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_cursor.py +0 -0
  77. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_debug.py +0 -0
  78. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_dev.py +0 -0
  79. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_eval.py +0 -0
  80. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_eval_bedrock.py +0 -0
  81. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_init.py +0 -0
  82. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_list_func.py +0 -0
  83. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_main_module.py +0 -0
  84. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_mcp_server.py +0 -0
  85. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_pull.py +0 -0
  86. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_push.py +0 -0
  87. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_push_happy.py +0 -0
  88. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_push_wrapper.py +0 -0
  89. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_registry.py +0 -0
  90. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/tests/test_utils.py +0 -0
  91. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/__init__.py +0 -0
  92. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/celebrate.py +0 -0
  93. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/config.py +0 -0
  94. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/cursor.py +0 -0
  95. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/docker.py +0 -0
  96. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/env_check.py +0 -0
  97. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/environment.py +0 -0
  98. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/git.py +0 -0
  99. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/interactive.py +0 -0
  100. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/local_runner.py +0 -0
  101. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/logging.py +0 -0
  102. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/metadata.py +0 -0
  103. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/package_runner.py +0 -0
  104. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/registry.py +0 -0
  105. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/remote_runner.py +0 -0
  106. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/runner.py +0 -0
  107. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/server.py +0 -0
  108. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/source_hash.py +0 -0
  109. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tasks.py +0 -0
  110. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/__init__.py +0 -0
  111. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_config.py +0 -0
  112. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_docker.py +0 -0
  113. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_docker_hints.py +0 -0
  114. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_env_check.py +0 -0
  115. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_environment.py +0 -0
  116. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_git.py +0 -0
  117. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_interactive_module.py +0 -0
  118. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_local_runner.py +0 -0
  119. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_logging_utils.py +0 -0
  120. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_metadata.py +0 -0
  121. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_package_runner.py +0 -0
  122. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_registry_utils.py +0 -0
  123. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_remote_runner.py +0 -0
  124. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_runner_modules.py +0 -0
  125. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_source_hash.py +0 -0
  126. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/tests/test_tasks.py +0 -0
  127. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/version_check.py +0 -0
  128. {hud_python-0.5.3 → hud_python-0.5.5}/hud/cli/utils/viewer.py +0 -0
  129. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/README.md +0 -0
  130. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/__init__.py +0 -0
  131. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/base.py +0 -0
  132. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/environment.py +0 -0
  133. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/fastmcp.py +0 -0
  134. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/mcp_use.py +0 -0
  135. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/__init__.py +0 -0
  136. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/test_analyze_scenarios.py +0 -0
  137. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/test_client_integration.py +0 -0
  138. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/test_fastmcp.py +0 -0
  139. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/test_mcp_use_retry.py +0 -0
  140. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/tests/test_protocol.py +0 -0
  141. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/utils/__init__.py +0 -0
  142. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/utils/mcp_use_retry.py +0 -0
  143. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/utils/retry.py +0 -0
  144. {hud_python-0.5.3 → hud_python-0.5.5}/hud/clients/utils/retry_transport.py +0 -0
  145. {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/__init__.py +0 -0
  146. {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/loader.py +0 -0
  147. {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/runner.py +0 -0
  148. {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/tests/__init__.py +0 -0
  149. {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/tests/test_loader.py +0 -0
  150. {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/tests/test_utils.py +0 -0
  151. {hud_python-0.5.3 → hud_python-0.5.5}/hud/datasets/utils.py +0 -0
  152. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/__init__.py +0 -0
  153. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connection.py +0 -0
  154. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/__init__.py +0 -0
  155. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/base.py +0 -0
  156. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/local.py +0 -0
  157. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/mcp_config.py +0 -0
  158. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/openai.py +0 -0
  159. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/connectors/remote.py +0 -0
  160. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/__init__.py +0 -0
  161. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/adk.py +0 -0
  162. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/anthropic.py +0 -0
  163. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/gemini.py +0 -0
  164. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/langchain.py +0 -0
  165. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/llamaindex.py +0 -0
  166. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/integrations/openai.py +0 -0
  167. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/mock.py +0 -0
  168. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/router.py +0 -0
  169. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/__init__.py +0 -0
  170. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_connection.py +0 -0
  171. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_connectors.py +0 -0
  172. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_environment.py +0 -0
  173. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_integrations.py +0 -0
  174. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_local_connectors.py +0 -0
  175. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_scenarios.py +0 -0
  176. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/tests/test_tools.py +0 -0
  177. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/types.py +0 -0
  178. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/utils/__init__.py +0 -0
  179. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/utils/formats.py +0 -0
  180. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/utils/schema.py +0 -0
  181. {hud_python-0.5.3 → hud_python-0.5.5}/hud/environment/utils/tool_wrappers.py +0 -0
  182. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/__init__.py +0 -0
  183. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/context.py +0 -0
  184. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/display.py +0 -0
  185. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/instrument.py +0 -0
  186. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/manager.py +0 -0
  187. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/parallel.py +0 -0
  188. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/task.py +0 -0
  189. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/__init__.py +0 -0
  190. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/test_context.py +0 -0
  191. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/test_eval.py +0 -0
  192. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/test_manager.py +0 -0
  193. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/test_parallel.py +0 -0
  194. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/tests/test_task.py +0 -0
  195. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/types.py +0 -0
  196. {hud_python-0.5.3 → hud_python-0.5.5}/hud/eval/utils.py +0 -0
  197. {hud_python-0.5.3 → hud_python-0.5.5}/hud/native/__init__.py +0 -0
  198. {hud_python-0.5.3 → hud_python-0.5.5}/hud/native/comparator.py +0 -0
  199. {hud_python-0.5.3 → hud_python-0.5.5}/hud/native/tests/__init__.py +0 -0
  200. {hud_python-0.5.3 → hud_python-0.5.5}/hud/native/tests/test_comparator.py +0 -0
  201. {hud_python-0.5.3 → hud_python-0.5.5}/hud/native/tests/test_native_init.py +0 -0
  202. {hud_python-0.5.3 → hud_python-0.5.5}/hud/patches/__init__.py +0 -0
  203. {hud_python-0.5.3 → hud_python-0.5.5}/hud/patches/mcp_patches.py +0 -0
  204. {hud_python-0.5.3 → hud_python-0.5.5}/hud/patches/warnings.py +0 -0
  205. {hud_python-0.5.3 → hud_python-0.5.5}/hud/py.typed +0 -0
  206. {hud_python-0.5.3 → hud_python-0.5.5}/hud/samples/__init__.py +0 -0
  207. {hud_python-0.5.3 → hud_python-0.5.5}/hud/samples/browser.py +0 -0
  208. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/__init__.py +0 -0
  209. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/context.py +0 -0
  210. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/helper/__init__.py +0 -0
  211. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/low_level.py +0 -0
  212. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/router.py +0 -0
  213. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/server.py +0 -0
  214. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/__init__.py +0 -0
  215. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_add_tool.py +0 -0
  216. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_context.py +0 -0
  217. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_mcp_server_handlers.py +0 -0
  218. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_mcp_server_integration.py +0 -0
  219. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_mcp_server_more.py +0 -0
  220. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_run_wrapper.py +0 -0
  221. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_server_extra.py +0 -0
  222. {hud_python-0.5.3 → hud_python-0.5.5}/hud/server/tests/test_sigterm_runner.py +0 -0
  223. {hud_python-0.5.3 → hud_python-0.5.5}/hud/settings.py +0 -0
  224. {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/__init__.py +0 -0
  225. {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/exceptions.py +0 -0
  226. {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/hints.py +0 -0
  227. {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/requests.py +0 -0
  228. {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/tests/__init__.py +0 -0
  229. {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/tests/test_exceptions.py +0 -0
  230. {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/tests/test_hints.py +0 -0
  231. {hud_python-0.5.3 → hud_python-0.5.5}/hud/shared/tests/test_requests.py +0 -0
  232. {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/__init__.py +0 -0
  233. {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/exporter.py +0 -0
  234. {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/instrument.py +0 -0
  235. {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/tests/__init__.py +0 -0
  236. {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/tests/test_eval_telemetry.py +0 -0
  237. {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/tests/test_exporter.py +0 -0
  238. {hud_python-0.5.3 → hud_python-0.5.5}/hud/telemetry/tests/test_instrument.py +0 -0
  239. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/__init__.py +0 -0
  240. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/agent.py +0 -0
  241. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/apply_patch.py +0 -0
  242. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/base.py +0 -0
  243. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/bash.py +0 -0
  244. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/__init__.py +0 -0
  245. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/anthropic.py +0 -0
  246. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/gemini.py +0 -0
  247. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/hud.py +0 -0
  248. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/openai.py +0 -0
  249. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/qwen.py +0 -0
  250. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/computer/settings.py +0 -0
  251. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/edit.py +0 -0
  252. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/__init__.py +0 -0
  253. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/base.py +0 -0
  254. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/pyautogui.py +0 -0
  255. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/tests/__init__.py +0 -0
  256. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/tests/test_base_executor.py +0 -0
  257. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/tests/test_pyautogui_executor.py +0 -0
  258. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/executors/xdo.py +0 -0
  259. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/__init__.py +0 -0
  260. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/config.py +0 -0
  261. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/grounded_tool.py +0 -0
  262. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/grounder.py +0 -0
  263. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/tests/__init__.py +0 -0
  264. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/grounding/tests/test_grounded_tool.py +0 -0
  265. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/jupyter.py +0 -0
  266. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/playwright.py +0 -0
  267. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/response.py +0 -0
  268. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/shell.py +0 -0
  269. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/submit.py +0 -0
  270. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/__init__.py +0 -0
  271. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_agent_tool.py +0 -0
  272. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_apply_patch.py +0 -0
  273. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_base.py +0 -0
  274. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_bash.py +0 -0
  275. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_bash_extended.py +0 -0
  276. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_computer.py +0 -0
  277. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_computer_actions.py +0 -0
  278. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_edit.py +0 -0
  279. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_init.py +0 -0
  280. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_jupyter_tool.py +0 -0
  281. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_playwright_tool.py +0 -0
  282. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_response.py +0 -0
  283. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_shell.py +0 -0
  284. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_submit.py +0 -0
  285. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_tools.py +0 -0
  286. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_tools_init.py +0 -0
  287. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_types.py +0 -0
  288. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/tests/test_utils.py +0 -0
  289. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/types.py +0 -0
  290. {hud_python-0.5.3 → hud_python-0.5.5}/hud/tools/utils.py +0 -0
  291. {hud_python-0.5.3 → hud_python-0.5.5}/hud/types.py +0 -0
  292. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/__init__.py +0 -0
  293. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/env.py +0 -0
  294. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/mcp.py +0 -0
  295. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/pretty_errors.py +0 -0
  296. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/strict_schema.py +0 -0
  297. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/telemetry.py +0 -0
  298. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/__init__.py +0 -0
  299. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_init.py +0 -0
  300. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_mcp.py +0 -0
  301. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_pretty_errors.py +0 -0
  302. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_telemetry.py +0 -0
  303. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tests/test_tool_shorthand.py +0 -0
  304. {hud_python-0.5.3 → hud_python-0.5.5}/hud/utils/tool_shorthand.py +0 -0
  305. {hud_python-0.5.3 → hud_python-0.5.5}/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.3
3
+ Version: 0.5.5
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
@@ -76,10 +76,18 @@ class ClaudeAgent(MCPAgent):
76
76
 
77
77
  model_client = self.config.model_client
78
78
  if model_client is None:
79
- api_key = settings.anthropic_api_key
80
- if not api_key:
81
- raise ValueError("Anthropic API key not found. Set ANTHROPIC_API_KEY.")
82
- model_client = AsyncAnthropic(api_key=api_key)
79
+ # Default to HUD gateway when HUD_API_KEY is available
80
+ if settings.api_key:
81
+ from hud.agents.gateway import build_gateway_client
82
+
83
+ model_client = build_gateway_client("anthropic")
84
+ elif settings.anthropic_api_key:
85
+ model_client = AsyncAnthropic(api_key=settings.anthropic_api_key)
86
+ else:
87
+ raise ValueError(
88
+ "No API key found. Set HUD_API_KEY for HUD gateway, "
89
+ "or ANTHROPIC_API_KEY for direct Anthropic access."
90
+ )
83
91
 
84
92
  self.anthropic_client = model_client
85
93
  self.max_tokens = self.config.max_tokens
@@ -61,10 +61,18 @@ class GeminiAgent(MCPAgent):
61
61
 
62
62
  model_client = self.config.model_client
63
63
  if model_client is None:
64
- api_key = settings.gemini_api_key
65
- if not api_key:
66
- raise ValueError("Gemini API key not found. Set GEMINI_API_KEY.")
67
- model_client = genai.Client(api_key=api_key)
64
+ # Default to HUD gateway when HUD_API_KEY is available
65
+ if settings.api_key:
66
+ from hud.agents.gateway import build_gateway_client
67
+
68
+ model_client = build_gateway_client("gemini")
69
+ elif settings.gemini_api_key:
70
+ model_client = genai.Client(api_key=settings.gemini_api_key)
71
+ else:
72
+ raise ValueError(
73
+ "No API key found. Set HUD_API_KEY for HUD gateway, "
74
+ "or GEMINI_API_KEY for direct Gemini access."
75
+ )
68
76
 
69
77
  if self.config.validate_api_key:
70
78
  try:
@@ -79,10 +79,18 @@ class OpenAIAgent(MCPAgent):
79
79
 
80
80
  model_client = self.config.model_client
81
81
  if model_client is None:
82
- api_key = settings.openai_api_key
83
- if not api_key:
84
- raise ValueError("OpenAI API key not found. Set OPENAI_API_KEY.")
85
- model_client = AsyncOpenAI(api_key=api_key)
82
+ # Default to HUD gateway when HUD_API_KEY is available
83
+ if settings.api_key:
84
+ from hud.agents.gateway import build_gateway_client
85
+
86
+ model_client = build_gateway_client("openai")
87
+ elif settings.openai_api_key:
88
+ model_client = AsyncOpenAI(api_key=settings.openai_api_key)
89
+ else:
90
+ raise ValueError(
91
+ "No API key found. Set HUD_API_KEY for HUD gateway, "
92
+ "or OPENAI_API_KEY for direct OpenAI access."
93
+ )
86
94
 
87
95
  if self.config.validate_api_key:
88
96
  try:
@@ -128,8 +128,9 @@ class TestOpenAIAgent:
128
128
  async def test_init_without_client_no_api_key(self) -> None:
129
129
  """Test agent initialization fails without API key."""
130
130
  with patch("hud.agents.openai.settings") as mock_settings:
131
+ mock_settings.api_key = None
131
132
  mock_settings.openai_api_key = None
132
- with pytest.raises(ValueError, match="OpenAI API key not found"):
133
+ with pytest.raises(ValueError, match="No API key found"):
133
134
  OpenAIAgent.create()
134
135
 
135
136
  @pytest.mark.asyncio
@@ -129,6 +129,7 @@ class Environment(
129
129
  super().__init__(name=name, instructions=instructions, **fastmcp_kwargs)
130
130
  self._connections: dict[str, Connector] = {}
131
131
  self._router = ToolRouter(conflict_resolution=conflict_resolution)
132
+ self._routing_built = False # Track if _build_routing has been called
132
133
  self._in_context = False
133
134
 
134
135
  # Tool call queues - run after connections established
@@ -224,6 +225,9 @@ class Environment(
224
225
  Automatically filters to only connections where the tool exists
225
226
  (based on cached_tools from initial discovery).
226
227
 
228
+ For internal tools (starting with _), tries ALL connections since
229
+ internal tools are hidden from list_tools() and won't be in cached_tools.
230
+
227
231
  Args:
228
232
  tool_name: Name of the tool to call
229
233
  **kwargs: Arguments to pass to the tool
@@ -233,10 +237,13 @@ class Environment(
233
237
  """
234
238
  import asyncio
235
239
 
236
- # Only call connections that have this tool
237
- targets = self._connections_with_tool(tool_name)
238
- if not targets:
239
- return {}
240
+ # For internal tools (underscore prefix), try ALL connections since
241
+ # they're hidden from list_tools() and won't appear in cached_tools.
242
+ # For regular tools, only try connections that advertise the tool.
243
+ if tool_name.startswith("_"):
244
+ targets = set(self._connections.keys())
245
+ else:
246
+ targets = self._connections_with_tool(tool_name)
240
247
 
241
248
  results: dict[str, Any] = {}
242
249
 
@@ -245,7 +252,8 @@ class Environment(
245
252
  if not connector or not connector.client:
246
253
  return
247
254
  try:
248
- results[name] = await connector.client.call_tool(tool_name, **kwargs)
255
+ # Use connector.call_tool which expects arguments as a dict
256
+ results[name] = await connector.call_tool(tool_name, kwargs)
249
257
  logger.debug("Broadcast '%s' to '%s' succeeded", tool_name, name)
250
258
  except Exception as e:
251
259
  results[name] = e
@@ -361,6 +369,7 @@ class Environment(
361
369
  if self._connections:
362
370
  await asyncio.gather(*[c.disconnect() for c in self._connections.values()])
363
371
  self._router.clear()
372
+ self._routing_built = False
364
373
 
365
374
  async def run_async(
366
375
  self,
@@ -389,6 +398,7 @@ class Environment(
389
398
  connections=self._connections,
390
399
  connection_order=list(self._connections.keys()),
391
400
  )
401
+ self._routing_built = True
392
402
  # Populate mock schemas for auto-generated mock values
393
403
  self._populate_mock_schemas()
394
404
 
@@ -406,6 +416,8 @@ class Environment(
406
416
 
407
417
  async def _env_list_tools(self) -> list[mcp_types.Tool]:
408
418
  """Return all tools including those from connectors."""
419
+ if not self._routing_built:
420
+ await self._build_routing()
409
421
  return self._router.tools
410
422
 
411
423
  async def _env_call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> list[Any]:
@@ -70,35 +70,17 @@ class ScenarioMixin:
70
70
  async def submit(self, scenario: str, answer: str) -> None:
71
71
  """Submit the agent's answer for a scenario's evaluate phase.
72
72
 
73
- This stores the answer locally and broadcasts to connected hubs
74
- that have the _hud_submit tool (auto-detected by Environment).
73
+ Stores locally and broadcasts to connected hubs with _hud_submit tool.
75
74
 
76
75
  Args:
77
76
  scenario: Name of the scenario (without env prefix)
78
77
  answer: The agent's answer/result to submit
79
-
80
- Example:
81
- # Direct call with scenario name
82
- await env.submit("checkout", "Order completed successfully")
83
-
84
- # Or via EvalContext (knows its own scenario)
85
- await ctx.submit("Order completed successfully")
86
78
  """
87
- # Store locally for our scenarios
88
79
  self._scenario_answers[scenario] = answer
89
- logger.debug(
90
- "Stored answer for scenario '%s': %s...",
91
- scenario,
92
- answer[:50] if len(answer) > 50 else answer,
93
- )
94
-
95
- # Broadcast to connections that have _hud_submit
96
- # Environment._broadcast_tool auto-filters to connections with the tool
97
- await self._broadcast_tool( # type: ignore[attr-defined]
98
- "_hud_submit",
99
- scenario=scenario,
100
- answer=answer,
101
- )
80
+ logger.debug("Stored answer for scenario '%s'", scenario)
81
+
82
+ # Broadcast to all connections (internal tools try all connections)
83
+ await self._broadcast_tool("_hud_submit", scenario=scenario, answer=answer) # type: ignore[attr-defined]
102
84
 
103
85
  def _register_hud_submit_tool(self) -> None:
104
86
  """Register the _hud_submit tool for receiving agent answers.
@@ -178,13 +160,9 @@ class ScenarioMixin:
178
160
  prompt_id = f"{safe_env_name}:{scenario_name}"
179
161
  logger.debug("Remote scenario (adding namespace): prompt_id=%s", prompt_id)
180
162
  # Serialize args for MCP prompt (only supports string values)
181
- # JSON-encode any non-string values so they can be deserialized on the other side
182
163
  serialized_args: dict[str, str] = {}
183
164
  for key, value in args.items():
184
- if isinstance(value, str):
185
- serialized_args[key] = value
186
- else:
187
- serialized_args[key] = json.dumps(value)
165
+ serialized_args[key] = value if isinstance(value, str) else json.dumps(value)
188
166
 
189
167
  try:
190
168
  result = await self.get_prompt(prompt_id, serialized_args) # type: ignore[attr-defined]
@@ -193,14 +171,26 @@ class ScenarioMixin:
193
171
  try:
194
172
  prompts = await self.list_prompts() # type: ignore[attr-defined]
195
173
  scenario_prompts = [p.name for p in prompts if ":" in p.name]
196
- available = (
197
- "\n ".join(scenario_prompts) if scenario_prompts else "(none found)"
198
- )
174
+ available = "\n ".join(scenario_prompts) if scenario_prompts else "(none)"
199
175
  except Exception:
200
- available = "(could not fetch available scenarios)"
176
+ available = "(could not fetch)"
177
+ scenario_prompts = []
178
+
179
+ original_error = str(e)
180
+ if prompt_id in scenario_prompts:
181
+ raise ValueError(
182
+ f"⚠️ ERROR: Scenario '{prompt_id}' exists but failed to execute.\n\n"
183
+ f"The scenario was found but encountered an error during setup:\n"
184
+ f" {original_error}\n\n"
185
+ f"This could be caused by:\n"
186
+ f" - Missing or invalid scenario arguments\n"
187
+ f" - An error in the scenario's setup function\n"
188
+ f" - Connection or serialization issues\n\n"
189
+ f"Check the scenario definition and required arguments."
190
+ ) from e
201
191
 
202
192
  raise ValueError(
203
- f"Scenario not found.\n\n"
193
+ f"⚠️ ERROR: Scenario not found.\n\n"
204
194
  f"Scenario IDs have the format 'environment_name:scenario_name'.\n"
205
195
  f"If you only specify 'scenario_name', the SDK uses your task's env name "
206
196
  f"as the prefix.\n"
@@ -212,7 +202,7 @@ class ScenarioMixin:
212
202
  f"Fix: Use one of the scenario IDs above in your task JSON."
213
203
  ) from e
214
204
 
215
- # Validate the response (outside try/except so errors aren't wrapped)
205
+ # Validate the response
216
206
  if result.messages:
217
207
  first_msg = result.messages[0]
218
208
  content = first_msg.content
@@ -275,23 +265,24 @@ class ScenarioMixin:
275
265
  del self._scenario_latest[scenario_name]
276
266
 
277
267
  # Remote scenario - read via MCP resource
278
- # If scenario_name already contains ":", it's already namespaced - use directly
279
268
  if ":" in scenario_name:
280
269
  resource_id = scenario_name
281
270
  else:
282
271
  env_name = getattr(self, "_source_env_name", None) or self.name
283
272
  safe_env_name = env_name.replace("_", "-")
284
273
  resource_id = f"{safe_env_name}:{scenario_name}"
274
+
285
275
  try:
286
276
  contents = await self.read_resource(resource_id) # type: ignore[attr-defined]
287
277
  if contents:
288
- first_content = contents[0]
289
- if hasattr(first_content, "text") and isinstance(first_content.text, str): # type: ignore[union-attr]
290
- data = json.loads(first_content.text) # type: ignore[union-attr]
278
+ first = contents[0]
279
+ if hasattr(first, "text") and isinstance(first.text, str): # type: ignore[union-attr]
280
+ data = json.loads(first.text) # type: ignore[union-attr]
291
281
  if "reward" in data:
292
282
  return float(data["reward"])
293
283
  except Exception as e:
294
284
  logger.warning("Failed to get scenario reward: %s", e)
285
+
295
286
  return None
296
287
 
297
288
  def scenario(
@@ -362,7 +353,7 @@ class ScenarioMixin:
362
353
  # Only include JSON-serializable defaults
363
354
  default_val = p.default
364
355
  if default_val is None or isinstance(
365
- default_val, (str, int, float, bool, list, dict)
356
+ default_val, (str | int | float | bool | list | dict)
366
357
  ):
367
358
  arg_info["default"] = default_val
368
359
 
@@ -412,27 +403,50 @@ class ScenarioMixin:
412
403
  from pydantic import TypeAdapter
413
404
 
414
405
  # Deserialize JSON-encoded arguments using Pydantic TypeAdapter
415
- # This properly handles: Pydantic models, enums, datetime, lists, dicts
406
+ # MCP prompts only support string arguments, so complex types are
407
+ # JSON-serialized on the sending side and deserialized here
416
408
  deserialized_args: dict[str, Any] = {}
417
409
  for arg_name, arg_value in handler_args.items():
418
410
  annotation = param_annotations.get(arg_name)
419
- if (
420
- annotation is not None
421
- and annotation is not str
422
- and isinstance(arg_value, str)
423
- ):
424
- # Try TypeAdapter.validate_json for proper type coercion
411
+
412
+ # Only attempt deserialization on string values
413
+ if not isinstance(arg_value, str):
414
+ deserialized_args[arg_name] = arg_value
415
+ continue
416
+
417
+ # If annotation is explicitly str, keep as string
418
+ if annotation is str:
419
+ deserialized_args[arg_name] = arg_value
420
+ continue
421
+
422
+ # If we have a non-str type annotation, use TypeAdapter
423
+ if annotation is not None:
425
424
  try:
426
425
  adapter = TypeAdapter(annotation)
427
426
  deserialized_args[arg_name] = adapter.validate_json(arg_value)
428
- except Exception:
429
- # Fall back to plain json.loads if TypeAdapter fails
430
- try:
431
- deserialized_args[arg_name] = json.loads(arg_value)
432
- except json.JSONDecodeError:
433
- deserialized_args[arg_name] = arg_value
434
- else:
435
- deserialized_args[arg_name] = arg_value
427
+ continue
428
+ except Exception: # noqa: S110
429
+ pass # Fall through to generic JSON decode
430
+
431
+ # Try JSON decode for strings that look like JSON
432
+ stripped = arg_value.strip()
433
+ if (stripped and stripped[0] in "[{") or stripped in ("true", "false", "null"):
434
+ try:
435
+ deserialized_args[arg_name] = json.loads(arg_value)
436
+ continue
437
+ except json.JSONDecodeError:
438
+ pass
439
+
440
+ # Try to decode if it looks like a number
441
+ if stripped.lstrip("-").replace(".", "", 1).isdigit():
442
+ try:
443
+ deserialized_args[arg_name] = json.loads(arg_value)
444
+ continue
445
+ except json.JSONDecodeError:
446
+ pass
447
+
448
+ # Keep as string
449
+ deserialized_args[arg_name] = arg_value
436
450
 
437
451
  # Create generator instance with deserialized args
438
452
  gen = scenario_fn(**deserialized_args)
@@ -21,6 +21,7 @@ import traceback
21
21
  from typing import TYPE_CHECKING, Any, Literal, Self
22
22
 
23
23
  from rich.console import Console
24
+ from rich.markup import escape
24
25
  from rich.panel import Panel
25
26
  from rich.table import Table
26
27
 
@@ -95,7 +96,7 @@ class HUDConsole:
95
96
  stderr: If True, output to stderr (default), otherwise stdout
96
97
  """
97
98
  console = self._stderr_console if stderr else self._stdout_console
98
- console.print(f"[{GREEN}]✅ {message}[/{GREEN}]")
99
+ console.print(f"[{GREEN}]✅ {escape(message)}[/{GREEN}]")
99
100
 
100
101
  def error(self, message: str, stderr: bool = True) -> None:
101
102
  """Print an error message.
@@ -106,10 +107,12 @@ class HUDConsole:
106
107
  """
107
108
  console = self._stderr_console if stderr else self._stdout_console
108
109
  tb = traceback.format_exc()
110
+ escaped_message = escape(message)
109
111
  if "NoneType: None" not in tb:
110
- console.print(f"[{RED} not bold]❌ {message}\n{tb}[/{RED} not bold]")
112
+ escaped_tb = escape(tb)
113
+ console.print(f"[{RED} not bold]❌ {escaped_message}\n{escaped_tb}[/{RED} not bold]")
111
114
  else:
112
- console.print(f"[{RED} not bold]❌ {message}[/{RED} not bold]")
115
+ console.print(f"[{RED} not bold]❌ {escaped_message}[/{RED} not bold]")
113
116
 
114
117
  def warning(self, message: str, stderr: bool = True) -> None:
115
118
  """Print a warning message.
@@ -119,7 +122,7 @@ class HUDConsole:
119
122
  stderr: If True, output to stderr (default), otherwise stdout
120
123
  """
121
124
  console = self._stderr_console if stderr else self._stdout_console
122
- console.print(f"⚠️ [{YELLOW} not bold]{message}[/{YELLOW} not bold]")
125
+ console.print(f"⚠️ [{YELLOW} not bold]{escape(message)}[/{YELLOW} not bold]")
123
126
 
124
127
  def info(self, message: str, stderr: bool = True) -> None:
125
128
  """Print an info message.
@@ -129,7 +132,7 @@ class HUDConsole:
129
132
  stderr: If True, output to stderr (default), otherwise stdout
130
133
  """
131
134
  console = self._stderr_console if stderr else self._stdout_console
132
- console.print(f"[{TEXT} not bold]{message}[/{TEXT} not bold]")
135
+ console.print(f"[{TEXT} not bold]{escape(message)}[/{TEXT} not bold]")
133
136
 
134
137
  def print(self, message: str, stderr: bool = True) -> None:
135
138
  """Print a message.
@@ -151,7 +154,7 @@ class HUDConsole:
151
154
  """
152
155
  console = self._stderr_console if stderr else self._stdout_console
153
156
  console.print(
154
- f"[{DIM} not bold][default]{label}[/default][/{DIM} not bold] [default]{value}[/default]" # noqa: E501
157
+ f"[{DIM} not bold][default]{escape(label)}[/default][/{DIM} not bold] [default]{escape(value)}[/default]" # noqa: E501
155
158
  )
156
159
 
157
160
  def link(self, url: str, stderr: bool = True) -> None:
@@ -162,7 +165,7 @@ class HUDConsole:
162
165
  stderr: If True, output to stderr (default), otherwise stdout
163
166
  """
164
167
  console = self._stderr_console if stderr else self._stdout_console
165
- console.print(f"[{SECONDARY} underline]{url}[/{SECONDARY} underline]")
168
+ console.print(f"[{SECONDARY} underline]{escape(url)}[/{SECONDARY} underline]")
166
169
 
167
170
  def json_config(self, json_str: str, stderr: bool = True) -> None:
168
171
  """Print JSON configuration with neutral theme.
@@ -173,7 +176,7 @@ class HUDConsole:
173
176
  """
174
177
  # Print JSON with neutral grey text
175
178
  console = self._stderr_console if stderr else self._stdout_console
176
- console.print(f"[{TEXT}]{json_str}[/{TEXT}]")
179
+ console.print(f"[{TEXT}]{escape(json_str)}[/{TEXT}]")
177
180
 
178
181
  def key_value_table(
179
182
  self, data: dict[str, str | int | float], show_header: bool = False, stderr: bool = True
@@ -203,7 +206,7 @@ class HUDConsole:
203
206
  stderr: If True, output to stderr (default), otherwise stdout
204
207
  """
205
208
  console = self._stderr_console if stderr else self._stdout_console
206
- console.print(f"[{DIM}]{message}[/{DIM}]")
209
+ console.print(f"[{DIM}]{escape(message)}[/{DIM}]")
207
210
 
208
211
  def phase(self, phase_num: int, title: str, stderr: bool = True) -> None:
209
212
  """Print a phase header (for debug command).
@@ -236,7 +239,7 @@ class HUDConsole:
236
239
  stderr: If True, output to stderr (default), otherwise stdout
237
240
  """
238
241
  console = self._stderr_console if stderr else self._stdout_console
239
- console.print(f"[rgb(181,137,0)]💡 Hint: {hint}[/rgb(181,137,0)]")
242
+ console.print(f"[rgb(181,137,0)]💡 Hint: {escape(hint)}[/rgb(181,137,0)]")
240
243
 
241
244
  def status_item(
242
245
  self,
@@ -265,10 +268,14 @@ class HUDConsole:
265
268
  indicator = indicators.get(status, indicators["info"])
266
269
  console = self._stderr_console if stderr else self._stdout_console
267
270
 
271
+ escaped_label = escape(label)
272
+ escaped_value = escape(value)
268
273
  if primary:
269
- console.print(f"{indicator} {label}: [bold {SECONDARY}]{value}[/bold {SECONDARY}]")
274
+ console.print(
275
+ f"{indicator} {escaped_label}: [bold {SECONDARY}]{escaped_value}[/bold {SECONDARY}]"
276
+ )
270
277
  else:
271
- console.print(f"{indicator} {label}: [{TEXT}]{value}[/{TEXT}]")
278
+ console.print(f"{indicator} {escaped_label}: [{TEXT}]{escaped_value}[/{TEXT}]")
272
279
 
273
280
  def command_example(
274
281
  self, command: str, description: str | None = None, stderr: bool = True
@@ -546,7 +553,12 @@ class HUDConsole:
546
553
  except (TypeError, ValueError):
547
554
  args_str = str(arguments)[:60]
548
555
 
549
- return f"[{GOLD}]→[/{GOLD}] [bold {TEXT}]{name}[/bold {TEXT}][{DIM}]({args_str})[/{DIM}]"
556
+ escaped_name = escape(name)
557
+ escaped_args = escape(args_str)
558
+ return (
559
+ f"[{GOLD}]→[/{GOLD}] [bold {TEXT}]{escaped_name}[/bold {TEXT}]"
560
+ f"[{DIM}]({escaped_args})[/{DIM}]"
561
+ )
550
562
 
551
563
  def format_tool_result(self, content: str, is_error: bool = False) -> str:
552
564
  """Format a tool result in compact HUD style.
@@ -562,11 +574,12 @@ class HUDConsole:
562
574
  if len(content) > 80:
563
575
  content = content[:77] + "..."
564
576
 
577
+ escaped_content = escape(content)
565
578
  # Format with status using HUD colors
566
579
  if is_error:
567
- return f" [{RED}]✗[/{RED}] [{DIM}]{content}[/{DIM}]"
580
+ return f" [{RED}]✗[/{RED}] [{DIM}]{escaped_content}[/{DIM}]"
568
581
  else:
569
- return f" [{GREEN}]✓[/{GREEN}] [{TEXT}]{content}[/{TEXT}]"
582
+ return f" [{GREEN}]✓[/{GREEN}] [{TEXT}]{escaped_content}[/{TEXT}]"
570
583
 
571
584
  def confirm(self, message: str, default: bool = True) -> bool:
572
585
  """Print a confirmation message.
@@ -590,12 +603,12 @@ class HUDConsole:
590
603
  stderr: If True, output to stderr
591
604
  """
592
605
  console = self._stderr_console if stderr else self._stdout_console
593
- console.print(f"[{color}]{symbol}[/{color}] {message}")
606
+ console.print(f"[{color}]{symbol}[/{color}] {escape(message)}")
594
607
 
595
608
  def detail(self, message: str, stderr: bool = True) -> None:
596
609
  """Print an indented detail line with gold pointer symbol."""
597
610
  console = self._stderr_console if stderr else self._stdout_console
598
- console.print(f" [{GOLD}]{Symbols.ITEM}[/{GOLD}] {message}")
611
+ console.print(f" [{GOLD}]{Symbols.ITEM}[/{GOLD}] {escape(message)}")
599
612
 
600
613
  def flow(self, message: str, stderr: bool = True) -> None:
601
614
  """Print a flow/transition message with wave symbol."""
@@ -5,4 +5,4 @@ def test_import():
5
5
  """Test that the package can be imported."""
6
6
  import hud
7
7
 
8
- assert hud.__version__ == "0.5.3"
8
+ assert hud.__version__ == "0.5.5"
@@ -4,4 +4,4 @@ Version information for the HUD SDK.
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- __version__ = "0.5.3"
7
+ __version__ = "0.5.5"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "hud-python"
3
- version = "0.5.3"
3
+ version = "0.5.5"
4
4
  description = "SDK for the HUD platform."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11, <3.13"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes