openhands-sdk 1.5.1__tar.gz → 1.10.0__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 (207) hide show
  1. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/PKG-INFO +9 -4
  2. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/__init__.py +11 -1
  3. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/agent.py +202 -31
  4. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/base.py +174 -84
  5. openhands_sdk-1.10.0/openhands/sdk/agent/prompts/model_specific/anthropic_claude.j2 +3 -0
  6. openhands_sdk-1.10.0/openhands/sdk/agent/prompts/model_specific/google_gemini.j2 +1 -0
  7. openhands_sdk-1.10.0/openhands/sdk/agent/prompts/model_specific/openai_gpt/gpt-5-codex.j2 +2 -0
  8. openhands_sdk-1.10.0/openhands/sdk/agent/prompts/model_specific/openai_gpt/gpt-5.j2 +3 -0
  9. openhands_sdk-1.10.0/openhands/sdk/agent/prompts/self_documentation.j2 +15 -0
  10. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/prompts/system_prompt.j2 +30 -1
  11. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/utils.py +21 -4
  12. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/__init__.py +2 -0
  13. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/agent_context.py +86 -12
  14. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/condenser/__init__.py +2 -0
  15. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/condenser/base.py +67 -11
  16. openhands_sdk-1.10.0/openhands/sdk/context/condenser/llm_summarizing_condenser.py +255 -0
  17. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/condenser/no_op_condenser.py +2 -1
  18. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/condenser/pipeline_condenser.py +10 -9
  19. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 +1 -5
  20. openhands_sdk-1.10.0/openhands/sdk/context/condenser/utils.py +149 -0
  21. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/prompts/prompt.py +40 -2
  22. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +4 -0
  23. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/prompts/templates/system_message_suffix.j2 +12 -3
  24. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/skills/__init__.py +14 -0
  25. openhands_sdk-1.10.0/openhands/sdk/context/skills/skill.py +975 -0
  26. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/skills/types.py +4 -0
  27. openhands_sdk-1.10.0/openhands/sdk/context/skills/utils.py +383 -0
  28. openhands_sdk-1.10.0/openhands/sdk/context/view.py +486 -0
  29. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/__init__.py +2 -0
  30. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/base.py +5 -0
  31. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/conversation.py +32 -3
  32. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/event_store.py +84 -12
  33. openhands_sdk-1.10.0/openhands/sdk/conversation/exceptions.py +68 -0
  34. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/impl/local_conversation.py +290 -19
  35. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/impl/remote_conversation.py +372 -15
  36. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/state.py +121 -21
  37. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/stuck_detector.py +81 -45
  38. openhands_sdk-1.10.0/openhands/sdk/conversation/types.py +45 -0
  39. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/visualizer/base.py +23 -0
  40. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/critic/__init__.py +4 -1
  41. openhands_sdk-1.10.0/openhands/sdk/critic/base.py +35 -0
  42. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/critic/impl/__init__.py +2 -0
  43. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/critic/impl/agent_finished.py +9 -5
  44. openhands_sdk-1.10.0/openhands/sdk/critic/impl/api/__init__.py +18 -0
  45. openhands_sdk-1.10.0/openhands/sdk/critic/impl/api/chat_template.py +232 -0
  46. openhands_sdk-1.10.0/openhands/sdk/critic/impl/api/client.py +313 -0
  47. openhands_sdk-1.10.0/openhands/sdk/critic/impl/api/critic.py +93 -0
  48. openhands_sdk-1.10.0/openhands/sdk/critic/impl/api/taxonomy.py +180 -0
  49. openhands_sdk-1.10.0/openhands/sdk/critic/result.py +148 -0
  50. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/condenser.py +52 -2
  51. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/conversation_error.py +12 -0
  52. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/llm_convertible/action.py +30 -0
  53. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/llm_convertible/message.py +10 -0
  54. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/llm_convertible/system.py +16 -20
  55. openhands_sdk-1.10.0/openhands/sdk/git/cached_repo.py +478 -0
  56. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/git/utils.py +149 -9
  57. openhands_sdk-1.10.0/openhands/sdk/hooks/__init__.py +38 -0
  58. openhands_sdk-1.10.0/openhands/sdk/hooks/config.py +329 -0
  59. openhands_sdk-1.10.0/openhands/sdk/hooks/conversation_hooks.py +274 -0
  60. openhands_sdk-1.10.0/openhands/sdk/hooks/executor.py +156 -0
  61. openhands_sdk-1.10.0/openhands/sdk/hooks/manager.py +170 -0
  62. openhands_sdk-1.10.0/openhands/sdk/hooks/types.py +40 -0
  63. openhands_sdk-1.10.0/openhands/sdk/io/base.py +100 -0
  64. openhands_sdk-1.10.0/openhands/sdk/io/cache.py +85 -0
  65. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/io/local.py +64 -2
  66. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/io/memory.py +34 -1
  67. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/llm.py +115 -93
  68. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/message.py +69 -30
  69. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/mixins/fn_call_converter.py +61 -16
  70. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/mixins/non_native_fc.py +5 -1
  71. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/options/chat_options.py +6 -2
  72. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/router/base.py +12 -0
  73. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/utils/model_features.py +68 -24
  74. openhands_sdk-1.10.0/openhands/sdk/llm/utils/model_prompt_spec.py +98 -0
  75. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/utils/telemetry.py +43 -4
  76. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/utils/verified_models.py +4 -5
  77. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/logger/logger.py +1 -1
  78. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/mcp/client.py +53 -6
  79. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/mcp/tool.py +27 -22
  80. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/mcp/utils.py +31 -23
  81. openhands_sdk-1.10.0/openhands/sdk/plugin/__init__.py +50 -0
  82. openhands_sdk-1.10.0/openhands/sdk/plugin/fetch.py +335 -0
  83. openhands_sdk-1.10.0/openhands/sdk/plugin/loader.py +111 -0
  84. openhands_sdk-1.10.0/openhands/sdk/plugin/plugin.py +498 -0
  85. openhands_sdk-1.10.0/openhands/sdk/plugin/types.py +781 -0
  86. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/secret/secrets.py +19 -4
  87. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/tool/__init__.py +7 -1
  88. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/tool/builtins/__init__.py +4 -0
  89. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/tool/registry.py +23 -0
  90. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/tool/schema.py +16 -3
  91. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/tool/tool.py +61 -10
  92. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/utils/__init__.py +2 -0
  93. openhands_sdk-1.10.0/openhands/sdk/utils/async_executor.py +115 -0
  94. openhands_sdk-1.10.0/openhands/sdk/utils/async_utils.py +74 -0
  95. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/utils/command.py +28 -1
  96. openhands_sdk-1.10.0/openhands/sdk/utils/models.py +296 -0
  97. openhands_sdk-1.10.0/openhands/sdk/utils/paging.py +63 -0
  98. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/workspace/base.py +22 -0
  99. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/workspace/local.py +16 -0
  100. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/workspace/remote/async_remote_workspace.py +16 -0
  101. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/workspace/remote/base.py +16 -0
  102. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands_sdk.egg-info/PKG-INFO +9 -4
  103. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands_sdk.egg-info/SOURCES.txt +56 -0
  104. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands_sdk.egg-info/requires.txt +4 -3
  105. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/pyproject.toml +11 -4
  106. openhands_sdk-1.5.1/openhands/sdk/context/condenser/llm_summarizing_condenser.py +0 -89
  107. openhands_sdk-1.5.1/openhands/sdk/context/skills/skill.py +0 -569
  108. openhands_sdk-1.5.1/openhands/sdk/context/view.py +0 -243
  109. openhands_sdk-1.5.1/openhands/sdk/conversation/exceptions.py +0 -25
  110. openhands_sdk-1.5.1/openhands/sdk/conversation/types.py +0 -15
  111. openhands_sdk-1.5.1/openhands/sdk/critic/base.py +0 -38
  112. openhands_sdk-1.5.1/openhands/sdk/io/base.py +0 -48
  113. openhands_sdk-1.5.1/openhands/sdk/utils/async_executor.py +0 -106
  114. openhands_sdk-1.5.1/openhands/sdk/utils/async_utils.py +0 -39
  115. openhands_sdk-1.5.1/openhands/sdk/utils/models.py +0 -570
  116. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/__init__.py +0 -0
  117. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/prompts/in_context_learning_example.j2 +0 -0
  118. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/prompts/in_context_learning_example_suffix.j2 +0 -0
  119. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/prompts/security_policy.j2 +0 -0
  120. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/prompts/security_risk_assessment.j2 +0 -0
  121. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/prompts/system_prompt_interactive.j2 +0 -0
  122. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/prompts/system_prompt_long_horizon.j2 +0 -0
  123. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/prompts/system_prompt_planning.j2 +0 -0
  124. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/agent/prompts/system_prompt_tech_philosophy.j2 +0 -0
  125. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/prompts/__init__.py +0 -0
  126. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
  127. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/skills/exceptions.py +0 -0
  128. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/context/skills/trigger.py +0 -0
  129. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/conversation_stats.py +0 -0
  130. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/events_list_base.py +0 -0
  131. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/fifo_lock.py +0 -0
  132. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/impl/__init__.py +0 -0
  133. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/persistence_const.py +0 -0
  134. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/response_utils.py +0 -0
  135. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/secret_registry.py +0 -0
  136. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/serialization_diff.py +0 -0
  137. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/title_utils.py +0 -0
  138. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
  139. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/conversation/visualizer/default.py +0 -0
  140. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/critic/impl/empty_patch.py +0 -0
  141. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/critic/impl/pass_critic.py +0 -0
  142. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/__init__.py +0 -0
  143. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/base.py +0 -0
  144. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/conversation_state.py +0 -0
  145. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/llm_completion_log.py +0 -0
  146. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
  147. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/llm_convertible/observation.py +0 -0
  148. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/token.py +0 -0
  149. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/types.py +0 -0
  150. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/event/user_action.py +0 -0
  151. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/git/exceptions.py +0 -0
  152. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/git/git_changes.py +0 -0
  153. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/git/git_diff.py +0 -0
  154. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/git/models.py +0 -0
  155. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/io/__init__.py +0 -0
  156. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/__init__.py +0 -0
  157. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/exceptions/__init__.py +0 -0
  158. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/exceptions/classifier.py +0 -0
  159. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/exceptions/mapping.py +0 -0
  160. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/exceptions/types.py +0 -0
  161. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/llm_registry.py +0 -0
  162. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/llm_response.py +0 -0
  163. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/options/__init__.py +0 -0
  164. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/options/common.py +0 -0
  165. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/options/responses_options.py +0 -0
  166. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/router/__init__.py +0 -0
  167. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
  168. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/router/impl/random.py +0 -0
  169. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/streaming.py +0 -0
  170. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/utils/metrics.py +0 -0
  171. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/utils/model_info.py +0 -0
  172. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
  173. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/llm/utils/unverified_models.py +0 -0
  174. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/logger/__init__.py +0 -0
  175. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/logger/rolling.py +0 -0
  176. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/mcp/__init__.py +0 -0
  177. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/mcp/definition.py +0 -0
  178. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/mcp/exceptions.py +0 -0
  179. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/observability/__init__.py +0 -0
  180. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/observability/laminar.py +0 -0
  181. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/observability/utils.py +0 -0
  182. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/py.typed +0 -0
  183. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/secret/__init__.py +0 -0
  184. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/security/__init__.py +0 -0
  185. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/security/analyzer.py +0 -0
  186. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/security/confirmation_policy.py +0 -0
  187. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/security/llm_analyzer.py +0 -0
  188. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/security/risk.py +0 -0
  189. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/tool/builtins/finish.py +0 -0
  190. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/tool/builtins/think.py +0 -0
  191. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/tool/spec.py +0 -0
  192. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/utils/cipher.py +0 -0
  193. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/utils/deprecation.py +0 -0
  194. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/utils/github.py +0 -0
  195. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/utils/json.py +0 -0
  196. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/utils/pydantic_diff.py +0 -0
  197. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/utils/pydantic_secrets.py +0 -0
  198. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/utils/truncate.py +0 -0
  199. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/utils/visualize.py +0 -0
  200. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/workspace/__init__.py +0 -0
  201. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/workspace/models.py +0 -0
  202. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/workspace/remote/__init__.py +0 -0
  203. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
  204. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands/sdk/workspace/workspace.py +0 -0
  205. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands_sdk.egg-info/dependency_links.txt +0 -0
  206. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/openhands_sdk.egg-info/top_level.txt +0 -0
  207. {openhands_sdk-1.5.1 → openhands_sdk-1.10.0}/setup.cfg +0 -0
@@ -1,17 +1,22 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-sdk
3
- Version: 1.5.1
3
+ Version: 1.10.0
4
4
  Summary: OpenHands SDK - Core functionality for building AI agents
5
+ Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
6
+ Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
7
+ Project-URL: Documentation, https://docs.openhands.dev/sdk
8
+ Project-URL: Bug Tracker, https://github.com/OpenHands/software-agent-sdk/issues
5
9
  Requires-Python: >=3.12
6
10
  Requires-Dist: deprecation>=2.1.0
7
11
  Requires-Dist: fastmcp>=2.11.3
12
+ Requires-Dist: filelock>=3.20.1
8
13
  Requires-Dist: httpx>=0.27.0
9
- Requires-Dist: litellm>=1.80.7
10
- Requires-Dist: pydantic>=2.11.7
14
+ Requires-Dist: litellm>=1.80.10
15
+ Requires-Dist: pydantic>=2.12.5
11
16
  Requires-Dist: python-frontmatter>=1.1.0
12
17
  Requires-Dist: python-json-logger>=3.3.0
13
18
  Requires-Dist: tenacity>=9.1.2
14
19
  Requires-Dist: websockets>=12
15
- Requires-Dist: lmnr>=0.7.20
20
+ Requires-Dist: lmnr>=0.7.24
16
21
  Provides-Extra: boto3
17
22
  Requires-Dist: boto3>=1.35.0; extra == "boto3"
@@ -1,7 +1,12 @@
1
1
  from importlib.metadata import PackageNotFoundError, version
2
2
 
3
3
  from openhands.sdk.agent import Agent, AgentBase
4
- from openhands.sdk.context import AgentContext
4
+ from openhands.sdk.context import (
5
+ AgentContext,
6
+ load_project_skills,
7
+ load_skills_from_dir,
8
+ load_user_skills,
9
+ )
5
10
  from openhands.sdk.context.condenser import (
6
11
  LLMSummarizingCondenser,
7
12
  )
@@ -36,6 +41,7 @@ from openhands.sdk.mcp import (
36
41
  MCPToolObservation,
37
42
  create_mcp_tools,
38
43
  )
44
+ from openhands.sdk.plugin import Plugin
39
45
  from openhands.sdk.tool import (
40
46
  Action,
41
47
  Observation,
@@ -93,11 +99,15 @@ __all__ = [
93
99
  "LLMSummarizingCondenser",
94
100
  "FileStore",
95
101
  "LocalFileStore",
102
+ "Plugin",
96
103
  "register_tool",
97
104
  "resolve_tool",
98
105
  "list_registered_tools",
99
106
  "Workspace",
100
107
  "LocalWorkspace",
101
108
  "RemoteWorkspace",
109
+ "load_project_skills",
110
+ "load_skills_from_dir",
111
+ "load_user_skills",
102
112
  "__version__",
103
113
  ]
@@ -17,6 +17,7 @@ from openhands.sdk.conversation import (
17
17
  LocalConversation,
18
18
  )
19
19
  from openhands.sdk.conversation.state import ConversationExecutionStatus
20
+ from openhands.sdk.critic.base import CriticResult
20
21
  from openhands.sdk.event import (
21
22
  ActionEvent,
22
23
  AgentErrorEvent,
@@ -25,8 +26,12 @@ from openhands.sdk.event import (
25
26
  ObservationEvent,
26
27
  SystemPromptEvent,
27
28
  TokenEvent,
29
+ UserRejectObservation,
30
+ )
31
+ from openhands.sdk.event.condenser import (
32
+ Condensation,
33
+ CondensationRequest,
28
34
  )
29
- from openhands.sdk.event.condenser import Condensation, CondensationRequest
30
35
  from openhands.sdk.llm import (
31
36
  LLMResponse,
32
37
  Message,
@@ -101,27 +106,103 @@ class Agent(AgentBase):
101
106
  # TODO(openhands): we should add test to test this init_state will actually
102
107
  # modify state in-place
103
108
 
104
- llm_convertible_messages = [
105
- event for event in state.events if isinstance(event, LLMConvertibleEvent)
106
- ]
107
- if len(llm_convertible_messages) == 0:
108
- # Prepare system message
109
- event = SystemPromptEvent(
110
- source="agent",
111
- system_prompt=TextContent(text=self.system_message),
112
- # Always expose a 'security_risk' parameter in tool schemas.
113
- # This ensures the schema remains consistent, even if the
114
- # security analyzer is disabled. Validation of this field
115
- # happens dynamically at runtime depending on the analyzer
116
- # configured. This allows weaker models to omit risk field
117
- # and bypass validation requirements when analyzer is disabled.
118
- # For detailed logic, see `_extract_security_risk` method.
119
- tools=[
120
- t.to_openai_tool(add_security_risk_prediction=True)
121
- for t in self.tools_map.values()
122
- ],
109
+ # Defensive check: Analyze state to detect unexpected initialization scenarios
110
+ # These checks help diagnose issues related to lazy loading and event ordering
111
+ # See: https://github.com/OpenHands/software-agent-sdk/issues/1785
112
+ events = list(state.events)
113
+ has_system_prompt = any(isinstance(e, SystemPromptEvent) for e in events)
114
+ has_user_message = any(
115
+ isinstance(e, MessageEvent) and e.source == "user" for e in events
116
+ )
117
+ has_any_llm_event = any(isinstance(e, LLMConvertibleEvent) for e in events)
118
+
119
+ # Log state for debugging initialization order issues
120
+ logger.debug(
121
+ f"init_state called: conversation_id={state.id}, "
122
+ f"event_count={len(events)}, "
123
+ f"has_system_prompt={has_system_prompt}, "
124
+ f"has_user_message={has_user_message}, "
125
+ f"has_any_llm_event={has_any_llm_event}"
126
+ )
127
+
128
+ if has_system_prompt:
129
+ # SystemPromptEvent already exists - this is unexpected during normal flow
130
+ # but could happen in persistence/resume scenarios
131
+ logger.warning(
132
+ f"init_state called but SystemPromptEvent already exists. "
133
+ f"conversation_id={state.id}, event_count={len(events)}. "
134
+ f"This may indicate double initialization or a resume scenario."
123
135
  )
124
- on_event(event)
136
+ return
137
+
138
+ # Assert: If there are user messages but no system prompt, something is wrong
139
+ # The system prompt should always be added before any user messages
140
+ if has_user_message:
141
+ event_types = [type(e).__name__ for e in events]
142
+ logger.error(
143
+ f"init_state: User message exists without SystemPromptEvent! "
144
+ f"conversation_id={state.id}, events={event_types}"
145
+ )
146
+ assert not has_user_message, (
147
+ f"Unexpected state: User message exists before SystemPromptEvent. "
148
+ f"conversation_id={state.id}, event_count={len(events)}, "
149
+ f"event_types={event_types}. "
150
+ f"This indicates an initialization order bug - init_state should be "
151
+ f"called before any user messages are added to the conversation."
152
+ )
153
+
154
+ # Prepare system message
155
+ event = SystemPromptEvent(
156
+ source="agent",
157
+ system_prompt=TextContent(text=self.system_message),
158
+ # Tools are stored as ToolDefinition objects and converted to
159
+ # OpenAI format with security_risk parameter during LLM completion.
160
+ # See make_llm_completion() in agent/utils.py for details.
161
+ tools=list(self.tools_map.values()),
162
+ )
163
+ on_event(event)
164
+
165
+ def _should_evaluate_with_critic(self, action: Action | None) -> bool:
166
+ """Determine if critic should evaluate based on action type and mode."""
167
+ if self.critic is None:
168
+ return False
169
+
170
+ if self.critic.mode == "all_actions":
171
+ return True
172
+
173
+ # For "finish_and_message" mode, only evaluate FinishAction
174
+ # (MessageEvent will be handled separately in step())
175
+ if isinstance(action, FinishAction):
176
+ return True
177
+
178
+ return False
179
+
180
+ def _evaluate_with_critic(
181
+ self, conversation: LocalConversation, event: ActionEvent | MessageEvent
182
+ ) -> CriticResult | None:
183
+ """Run critic evaluation on the current event and history."""
184
+ if self.critic is None:
185
+ return None
186
+
187
+ try:
188
+ # Build event history including the current event
189
+ events = list(conversation.state.events) + [event]
190
+ llm_convertible_events = [
191
+ e for e in events if isinstance(e, LLMConvertibleEvent)
192
+ ]
193
+
194
+ # Evaluate without git_patch for now
195
+ critic_result = self.critic.evaluate(
196
+ events=llm_convertible_events, git_patch=None
197
+ )
198
+ logger.info(
199
+ f"✓ Critic evaluation: score={critic_result.score:.3f}, "
200
+ f"success={critic_result.success}"
201
+ )
202
+ return critic_result
203
+ except Exception as e:
204
+ logger.error(f"✗ Critic evaluation failed: {e}", exc_info=True)
205
+ return None
125
206
 
126
207
  def _execute_actions(
127
208
  self,
@@ -151,9 +232,20 @@ class Agent(AgentBase):
151
232
  self._execute_actions(conversation, pending_actions, on_event)
152
233
  return
153
234
 
235
+ # Check if the last user message was blocked by a UserPromptSubmit hook
236
+ # If so, skip processing and mark conversation as finished
237
+ for event in reversed(list(state.events)):
238
+ if isinstance(event, MessageEvent) and event.source == "user":
239
+ reason = state.pop_blocked_message(event.id)
240
+ if reason is not None:
241
+ logger.info(f"User message blocked by hook: {reason}")
242
+ state.execution_status = ConversationExecutionStatus.FINISHED
243
+ return
244
+ break # Only check the most recent user message
245
+
154
246
  # Prepare LLM messages using the utility function
155
247
  _messages_or_condensation = prepare_llm_messages(
156
- state.events, condenser=self.condenser
248
+ state.events, condenser=self.condenser, llm=self.llm
157
249
  )
158
250
 
159
251
  # Process condensation event before agent sampels another action
@@ -229,6 +321,7 @@ class Agent(AgentBase):
229
321
  for i, tool_call in enumerate(message.tool_calls):
230
322
  action_event = self._get_action_event(
231
323
  tool_call,
324
+ conversation=conversation,
232
325
  llm_response_id=llm_response.id,
233
326
  on_event=on_event,
234
327
  security_analyzer=state.security_analyzer,
@@ -267,6 +360,14 @@ class Agent(AgentBase):
267
360
  llm_message=message,
268
361
  llm_response_id=llm_response.id,
269
362
  )
363
+ # Run critic evaluation if configured for finish_and_message mode
364
+ if self.critic is not None and self.critic.mode == "finish_and_message":
365
+ critic_result = self._evaluate_with_critic(conversation, msg_event)
366
+ if critic_result is not None:
367
+ # Create new event with critic result
368
+ msg_event = msg_event.model_copy(
369
+ update={"critic_result": critic_result}
370
+ )
270
371
  on_event(msg_event)
271
372
 
272
373
  # Emit VLLM token ids if enabled
@@ -354,9 +455,34 @@ class Agent(AgentBase):
354
455
  security_risk = risk.SecurityRisk(raw)
355
456
  return security_risk
356
457
 
458
+ def _extract_summary(self, tool_name: str, arguments: dict) -> str:
459
+ """Extract and validate the summary field from tool arguments.
460
+
461
+ Summary field is always requested but optional - if LLM doesn't provide
462
+ it or provides invalid data, we generate a default summary using the
463
+ tool name and arguments.
464
+
465
+ Args:
466
+ tool_name: Name of the tool being called
467
+ arguments: Dictionary of tool arguments from LLM
468
+
469
+ Returns:
470
+ The summary string - either from LLM or a default generated one
471
+ """
472
+ summary = arguments.pop("summary", None)
473
+
474
+ # If valid summary provided by LLM, use it
475
+ if summary is not None and isinstance(summary, str) and summary.strip():
476
+ return summary
477
+
478
+ # Generate default summary: {tool_name}: {arguments}
479
+ args_str = json.dumps(arguments)
480
+ return f"{tool_name}: {args_str}"
481
+
357
482
  def _get_action_event(
358
483
  self,
359
484
  tool_call: MessageToolCall,
485
+ conversation: LocalConversation,
360
486
  llm_response_id: str,
361
487
  on_event: ConversationCallbackType,
362
488
  security_analyzer: analyzer.SecurityAnalyzerBase | None = None,
@@ -415,6 +541,8 @@ class Agent(AgentBase):
415
541
  "Unexpected 'security_risk' key found in tool arguments"
416
542
  )
417
543
 
544
+ summary = self._extract_summary(tool.name, arguments)
545
+
418
546
  action: Action = tool.action_from_arguments(arguments)
419
547
  except (json.JSONDecodeError, ValidationError, ValueError) as e:
420
548
  err = (
@@ -443,6 +571,7 @@ class Agent(AgentBase):
443
571
  on_event(event)
444
572
  return
445
573
 
574
+ # Create initial action event
446
575
  action_event = ActionEvent(
447
576
  action=action,
448
577
  thought=thought or [],
@@ -454,7 +583,18 @@ class Agent(AgentBase):
454
583
  tool_call=tool_call,
455
584
  llm_response_id=llm_response_id,
456
585
  security_risk=security_risk,
586
+ summary=summary,
457
587
  )
588
+
589
+ # Run critic evaluation if configured
590
+ if self._should_evaluate_with_critic(action):
591
+ critic_result = self._evaluate_with_critic(conversation, action_event)
592
+ if critic_result is not None:
593
+ # Create new event with critic result
594
+ action_event = action_event.model_copy(
595
+ update={"critic_result": critic_result}
596
+ )
597
+
458
598
  on_event(action_event)
459
599
  return action_event
460
600
 
@@ -469,8 +609,26 @@ class Agent(AgentBase):
469
609
 
470
610
  It will call the tool's executor and update the state & call callback fn
471
611
  with the observation.
612
+
613
+ If the action was blocked by a PreToolUse hook (recorded in
614
+ state.blocked_actions), a UserRejectObservation is emitted instead
615
+ of executing the action.
472
616
  """
473
617
  state = conversation.state
618
+
619
+ # Check if this action was blocked by a PreToolUse hook
620
+ reason = state.pop_blocked_action(action_event.id)
621
+ if reason is not None:
622
+ logger.info(f"Action '{action_event.tool_name}' blocked by hook: {reason}")
623
+ rejection = UserRejectObservation(
624
+ action_id=action_event.id,
625
+ tool_name=action_event.tool_name,
626
+ tool_call_id=action_event.tool_call_id,
627
+ rejection_reason=reason,
628
+ )
629
+ on_event(rejection)
630
+ return rejection
631
+
474
632
  tool = self.tools_map.get(action_event.tool_name, None)
475
633
  if tool is None:
476
634
  raise RuntimeError(
@@ -479,16 +637,29 @@ class Agent(AgentBase):
479
637
  )
480
638
 
481
639
  # Execute actions!
482
- if should_enable_observability():
483
- tool_name = extract_action_name(action_event)
484
- observation: Observation = observe(name=tool_name, span_type="TOOL")(tool)(
485
- action_event.action, conversation
640
+ try:
641
+ if should_enable_observability():
642
+ tool_name = extract_action_name(action_event)
643
+ observation: Observation = observe(name=tool_name, span_type="TOOL")(
644
+ tool
645
+ )(action_event.action, conversation)
646
+ else:
647
+ observation = tool(action_event.action, conversation)
648
+ assert isinstance(observation, Observation), (
649
+ f"Tool '{tool.name}' executor must return an Observation"
486
650
  )
487
- else:
488
- observation = tool(action_event.action, conversation)
489
- assert isinstance(observation, Observation), (
490
- f"Tool '{tool.name}' executor must return an Observation"
491
- )
651
+ except ValueError as e:
652
+ # Tool execution raised a ValueError (e.g., invalid argument combination)
653
+ # Convert to AgentErrorEvent so the agent can correct itself
654
+ err = f"Error executing tool '{tool.name}': {e}"
655
+ logger.warning(err)
656
+ error_event = AgentErrorEvent(
657
+ error=err,
658
+ tool_name=tool.name,
659
+ tool_call_id=action_event.tool_call.id,
660
+ )
661
+ on_event(error_event)
662
+ return error_event
492
663
 
493
664
  obs_event = ObservationEvent(
494
665
  observation=observation,