openhands-sdk 1.49.6__tar.gz → 1.50.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 (311) hide show
  1. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/PKG-INFO +1 -1
  2. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/agent.py +70 -3
  3. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/response_dispatch.py +24 -3
  4. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +65 -20
  5. openhands_sdk-1.50.0/openhands/sdk/context/condenser/prompts/summarizing_events.j2 +7 -0
  6. openhands_sdk-1.49.6/openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 → openhands_sdk-1.50.0/openhands/sdk/context/condenser/prompts/summarizing_system.j2 +0 -8
  7. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/goal/judge.py +12 -3
  8. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/goal/prompts.py +31 -13
  9. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/impl/local_conversation.py +46 -33
  10. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/title_utils.py +22 -3
  11. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/conversation_hooks.py +4 -0
  12. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/executor.py +9 -1
  13. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/manager.py +3 -0
  14. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/__init__.py +2 -0
  15. openhands_sdk-1.50.0/openhands/sdk/llm/call_context.py +86 -0
  16. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/exceptions/classifier.py +26 -10
  17. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/fallback_strategy.py +13 -1
  18. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/llm.py +26 -27
  19. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/options/chat_options.py +11 -3
  20. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/options/common.py +1 -29
  21. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/options/responses_options.py +11 -3
  22. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/model_features.py +7 -0
  23. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/telemetry.py +5 -1
  24. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/grayswan/analyzer.py +25 -3
  25. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/classify_and_switch_llm.py +4 -2
  26. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/schema.py +5 -1
  27. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands_sdk.egg-info/PKG-INFO +1 -1
  28. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands_sdk.egg-info/SOURCES.txt +6 -2
  29. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/pyproject.toml +1 -1
  30. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/__init__.py +0 -0
  31. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/__init__.py +0 -0
  32. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/acp_agent.py +0 -0
  33. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/acp_file_credentials.py +0 -0
  34. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/acp_models.py +0 -0
  35. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/acp_tracing.py +0 -0
  36. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/base.py +0 -0
  37. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/critic_mixin.py +0 -0
  38. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/parallel_executor.py +0 -0
  39. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/stream_context.py +0 -0
  40. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/agent/utils.py +0 -0
  41. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/banner.py +0 -0
  42. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/__init__.py +0 -0
  43. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/agent_context.py +0 -0
  44. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/__init__.py +0 -0
  45. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/base.py +0 -0
  46. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
  47. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
  48. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/condenser/utils.py +0 -0
  49. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/memory.py +0 -0
  50. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/__init__.py +0 -0
  51. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/presets.py +0 -0
  52. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/prompt.py +0 -0
  53. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/registry.py +0 -0
  54. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/section.py +0 -0
  55. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
  56. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
  57. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/sections/planning.py +0 -0
  58. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/sections/static.py +0 -0
  59. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
  60. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
  61. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/__init__.py +0 -0
  62. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/manipulation_indices.py +0 -0
  63. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/__init__.py +0 -0
  64. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/base.py +0 -0
  65. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
  66. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
  67. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
  68. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
  69. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/context/view/view.py +0 -0
  70. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/__init__.py +0 -0
  71. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/base.py +0 -0
  72. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/cancellation.py +0 -0
  73. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/conversation.py +0 -0
  74. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/conversation_stats.py +0 -0
  75. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/event_store.py +0 -0
  76. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/events_list_base.py +0 -0
  77. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/exceptions.py +0 -0
  78. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/fifo_lock.py +0 -0
  79. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/goal/__init__.py +0 -0
  80. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/goal/controller.py +0 -0
  81. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/goal/runner.py +0 -0
  82. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/impl/__init__.py +0 -0
  83. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/impl/remote_conversation.py +0 -0
  84. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/persistence_const.py +0 -0
  85. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/request.py +0 -0
  86. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
  87. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/response_utils.py +0 -0
  88. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/secret_registry.py +0 -0
  89. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/state.py +0 -0
  90. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/stuck_detector.py +0 -0
  91. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/types.py +0 -0
  92. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
  93. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/visualizer/base.py +0 -0
  94. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/conversation/visualizer/default.py +0 -0
  95. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/credential.py +0 -0
  96. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/__init__.py +0 -0
  97. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/base.py +0 -0
  98. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/__init__.py +0 -0
  99. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/agent_finished.py +0 -0
  100. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/api/__init__.py +0 -0
  101. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
  102. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/api/client.py +0 -0
  103. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/api/critic.py +0 -0
  104. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
  105. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/empty_patch.py +0 -0
  106. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/impl/pass_critic.py +0 -0
  107. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/critic/result.py +0 -0
  108. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/__init__.py +0 -0
  109. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/acp_tool_call.py +0 -0
  110. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/base.py +0 -0
  111. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/condenser.py +0 -0
  112. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/conversation_error.py +0 -0
  113. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/conversation_state.py +0 -0
  114. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/error_classification.py +0 -0
  115. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/hook_execution.py +0 -0
  116. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_completion_log.py +0 -0
  117. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
  118. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/action.py +0 -0
  119. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/message.py +0 -0
  120. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/observation.py +0 -0
  121. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
  122. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/llm_convertible/system.py +0 -0
  123. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/resume_transcript.py +0 -0
  124. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/streaming_delta.py +0 -0
  125. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/token.py +0 -0
  126. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/types.py +0 -0
  127. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/event/user_action.py +0 -0
  128. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/__init__.py +0 -0
  129. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/fetch.py +0 -0
  130. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/__init__.py +0 -0
  131. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/info.py +0 -0
  132. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/interface.py +0 -0
  133. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/manager.py +0 -0
  134. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/metadata.py +0 -0
  135. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/extensions/installation/utils.py +0 -0
  136. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/cached_repo.py +0 -0
  137. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/exceptions.py +0 -0
  138. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/git_changes.py +0 -0
  139. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/git_commits.py +0 -0
  140. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/git_diff.py +0 -0
  141. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/models.py +0 -0
  142. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/git/utils.py +0 -0
  143. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/__init__.py +0 -0
  144. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/config.py +0 -0
  145. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/hooks/types.py +0 -0
  146. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/io/__init__.py +0 -0
  147. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/io/base.py +0 -0
  148. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/io/cache.py +0 -0
  149. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/io/local.py +0 -0
  150. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/io/memory.py +0 -0
  151. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/auth/__init__.py +0 -0
  152. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/auth/credentials.py +0 -0
  153. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/auth/openai.py +0 -0
  154. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/cleanup_profile.py +0 -0
  155. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/exceptions/__init__.py +0 -0
  156. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/exceptions/mapping.py +0 -0
  157. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/exceptions/types.py +0 -0
  158. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/llm_profile_store.py +0 -0
  159. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/llm_registry.py +0 -0
  160. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/llm_response.py +0 -0
  161. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/message.py +0 -0
  162. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/meta_profile_store.py +0 -0
  163. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
  164. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
  165. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
  166. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/options/__init__.py +0 -0
  167. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/provider_connection_store.py +0 -0
  168. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/router/__init__.py +0 -0
  169. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/router/base.py +0 -0
  170. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
  171. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/router/impl/random.py +0 -0
  172. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/streaming.py +0 -0
  173. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/image_inline.py +0 -0
  174. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/image_resize.py +0 -0
  175. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
  176. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/metrics.py +0 -0
  177. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/model_info.py +0 -0
  178. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
  179. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
  180. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
  181. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
  182. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
  183. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
  184. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
  185. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/unverified_models.py +0 -0
  186. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/verified_models.py +0 -0
  187. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
  188. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/logger/__init__.py +0 -0
  189. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/logger/logger.py +0 -0
  190. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/logger/rolling.py +0 -0
  191. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/marketplace/__init__.py +0 -0
  192. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/marketplace/registration.py +0 -0
  193. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/marketplace/registry.py +0 -0
  194. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/marketplace/types.py +0 -0
  195. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/__init__.py +0 -0
  196. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/client.py +0 -0
  197. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/config.py +0 -0
  198. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/definition.py +0 -0
  199. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/exceptions.py +0 -0
  200. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/oauth.py +0 -0
  201. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/tool.py +0 -0
  202. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/mcp/utils.py +0 -0
  203. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/observability/__init__.py +0 -0
  204. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/observability/laminar.py +0 -0
  205. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/observability/utils.py +0 -0
  206. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/__init__.py +0 -0
  207. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/discovery.py +0 -0
  208. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/fetch.py +0 -0
  209. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/__init__.py +0 -0
  210. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/agent_plugins.py +0 -0
  211. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/agent_plugins_mcp.py +0 -0
  212. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/base.py +0 -0
  213. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/claude_code.py +0 -0
  214. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/schemas/mcp-1.0.0.schema.json +0 -0
  215. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
  216. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/installed.py +0 -0
  217. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/loader.py +0 -0
  218. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/plugin.py +0 -0
  219. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/source.py +0 -0
  220. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/plugin/types.py +0 -0
  221. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/__init__.py +0 -0
  222. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/agent_profile.py +0 -0
  223. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/agent_profile_store.py +0 -0
  224. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/profile_refs.py +0 -0
  225. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/resolver.py +0 -0
  226. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/profiles/seed.py +0 -0
  227. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/py.typed +0 -0
  228. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/secret/__init__.py +0 -0
  229. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/secret/secrets.py +0 -0
  230. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/__init__.py +0 -0
  231. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/_shell_ast.py +0 -0
  232. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/analyzer.py +0 -0
  233. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/confirmation_policy.py +0 -0
  234. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
  235. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
  236. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
  237. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -0
  238. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
  239. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/ensemble.py +0 -0
  240. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/grayswan/__init__.py +0 -0
  241. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/grayswan/utils.py +0 -0
  242. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/llm_analyzer.py +0 -0
  243. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/risk.py +0 -0
  244. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/shell_parser.py +0 -0
  245. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/toolshield_helpers.py +0 -0
  246. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
  247. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/__init__.py +0 -0
  248. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/acp_install_catalog.py +0 -0
  249. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/acp_providers.py +0 -0
  250. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/api_models.py +0 -0
  251. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/metadata.py +0 -0
  252. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/settings/model.py +0 -0
  253. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/__init__.py +0 -0
  254. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/exceptions.py +0 -0
  255. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/execute.py +0 -0
  256. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/fetch.py +0 -0
  257. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/installed.py +0 -0
  258. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/skill.py +0 -0
  259. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/trigger.py +0 -0
  260. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/types.py +0 -0
  261. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/skills/utils.py +0 -0
  262. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/subagent/__init__.py +0 -0
  263. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/subagent/load.py +0 -0
  264. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/subagent/registry.py +0 -0
  265. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/subagent/schema.py +0 -0
  266. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/testing/__init__.py +0 -0
  267. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/testing/test_llm.py +0 -0
  268. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/__init__.py +0 -0
  269. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/__init__.py +0 -0
  270. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/finish.py +0 -0
  271. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
  272. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
  273. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/think.py +0 -0
  274. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
  275. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/client_tool.py +0 -0
  276. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/defaults.py +0 -0
  277. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/registry.py +0 -0
  278. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/spec.py +0 -0
  279. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/tool/tool.py +0 -0
  280. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/__init__.py +0 -0
  281. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/async_executor.py +0 -0
  282. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/async_utils.py +0 -0
  283. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/cipher.py +0 -0
  284. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/command.py +0 -0
  285. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/datetime.py +0 -0
  286. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/deprecation.py +0 -0
  287. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/files.py +0 -0
  288. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/github.py +0 -0
  289. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/json.py +0 -0
  290. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/masking.py +0 -0
  291. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/models.py +0 -0
  292. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/paging.py +0 -0
  293. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/path.py +0 -0
  294. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/pydantic_secrets.py +0 -0
  295. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/redact.py +0 -0
  296. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/truncate.py +0 -0
  297. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/utils/visualize.py +0 -0
  298. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/__init__.py +0 -0
  299. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/base.py +0 -0
  300. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/local.py +0 -0
  301. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/models.py +0 -0
  302. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/remote/__init__.py +0 -0
  303. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
  304. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/remote/base.py +0 -0
  305. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
  306. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/repo.py +0 -0
  307. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands/sdk/workspace/workspace.py +0 -0
  308. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands_sdk.egg-info/dependency_links.txt +0 -0
  309. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands_sdk.egg-info/requires.txt +0 -0
  310. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/openhands_sdk.egg-info/top_level.txt +0 -0
  311. {openhands_sdk-1.49.6 → openhands_sdk-1.50.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-sdk
3
- Version: 1.49.6
3
+ Version: 1.50.0
4
4
  Summary: OpenHands SDK - Core functionality for building AI agents
5
5
  Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
6
6
  Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import asyncio
3
4
  import json
4
5
  import re
5
6
  from collections.abc import Callable
@@ -61,6 +62,7 @@ from openhands.sdk.llm import (
61
62
  TextContent,
62
63
  ThinkingBlock,
63
64
  )
65
+ from openhands.sdk.llm.call_context import llm_call_context_scope
64
66
  from openhands.sdk.llm.exceptions import (
65
67
  FunctionCallValidationError,
66
68
  LLMContentPolicyViolationError,
@@ -120,6 +122,18 @@ INIT_STATE_PREFIX_SCAN_WINDOW = 3
120
122
 
121
123
 
122
124
  def _latest_user_message_contains_image(messages: list[Message]) -> bool:
125
+ """Check if the most recent user message contains image content.
126
+
127
+ Scans the message list in reverse order to find the latest user message
128
+ and checks whether it contains any image attachments.
129
+
130
+ Args:
131
+ messages: List of conversation messages to search.
132
+
133
+ Returns:
134
+ True if the most recent user message contains images, False otherwise.
135
+ Returns False if no user messages are found.
136
+ """
123
137
  for message in reversed(messages):
124
138
  if message.role == "user":
125
139
  return message.contains_image
@@ -127,6 +141,18 @@ def _latest_user_message_contains_image(messages: list[Message]) -> bool:
127
141
 
128
142
 
129
143
  def _non_multimodal_image_message(model: str) -> Message:
144
+ """Create an error message for when images are sent to a non-vision model.
145
+
146
+ Constructs a user-friendly assistant message explaining that the current
147
+ model does not support image understanding and suggesting the user switch
148
+ to a multimodal model.
149
+
150
+ Args:
151
+ model: The name of the current model that does not support vision.
152
+
153
+ Returns:
154
+ A Message object containing an assistant response with the error explanation.
155
+ """
130
156
  return Message(
131
157
  role="assistant",
132
158
  content=[
@@ -144,6 +170,20 @@ def _non_multimodal_image_message(model: str) -> Message:
144
170
  def _replace_latest_user_images_with_references(
145
171
  messages: list[Message],
146
172
  ) -> list[Message]:
173
+ """Replace images in the most recent user message containing images.
174
+
175
+ Searches backward, skipping user messages without images. Each image URL
176
+ becomes a textual reference to the inspect_image_with_vision tool, with a
177
+ zero-based image index within the selected message.
178
+
179
+ Args:
180
+ messages: List of conversation messages to process.
181
+
182
+ Returns:
183
+ A new list with the selected message copied and its images replaced.
184
+ Other messages and non-image content are unchanged. If no user message
185
+ contains images, returns a shallow copy of the original list.
186
+ """
147
187
  rewritten = list(messages)
148
188
  for index in range(len(rewritten) - 1, -1, -1):
149
189
  message = rewritten[index]
@@ -639,7 +679,11 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
639
679
  on_event: ConversationCallbackType,
640
680
  on_token: ConversationTokenCallbackType | None = None,
641
681
  ) -> None:
642
- with StreamContext.open(conversation, on_token) as stream:
682
+ call_context = conversation.get_llm_call_context()
683
+ with (
684
+ llm_call_context_scope(call_context),
685
+ StreamContext.open(conversation, on_token) as stream,
686
+ ):
643
687
  self._step(conversation, on_event, stream)
644
688
 
645
689
  def _step(
@@ -849,7 +893,11 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
849
893
  parallel calls with :func:`asyncio.gather`, keeping the event
850
894
  loop responsive during blocking tool I/O.
851
895
  """
852
- with StreamContext.open(conversation, on_token) as stream:
896
+ call_context = conversation.get_llm_call_context()
897
+ with (
898
+ llm_call_context_scope(call_context),
899
+ StreamContext.open(conversation, on_token) as stream,
900
+ ):
853
901
  await self._astep(conversation, on_event, stream)
854
902
 
855
903
  async def _astep(
@@ -1022,6 +1070,18 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1022
1070
 
1023
1071
  message: Message = llm_response.message
1024
1072
  response_type = classify_response(message)
1073
+ if response_type is not LLMResponseType.TOOL_CALLS:
1074
+ # Resolve outside the event loop and state lock. A lookup may call
1075
+ # this server, and update_secrets() may register another source while
1076
+ # we await it. Repeat until the registry is stable under the lock.
1077
+ while True:
1078
+ sources = dict(state.secret_registry.secret_sources)
1079
+ async with conversation._released_state_lock_during_io():
1080
+ message = await asyncio.to_thread(
1081
+ self._mask_secrets, message, conversation
1082
+ )
1083
+ if sources == state.secret_registry.secret_sources:
1084
+ break
1025
1085
 
1026
1086
  match response_type:
1027
1087
  case LLMResponseType.TOOL_CALLS:
@@ -1030,7 +1090,13 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1030
1090
  )
1031
1091
  case LLMResponseType.CONTENT:
1032
1092
  self._handle_content_response(
1033
- message, llm_response, conversation, state, on_event, stream
1093
+ message,
1094
+ llm_response,
1095
+ conversation,
1096
+ state,
1097
+ on_event,
1098
+ stream,
1099
+ mask_secrets=False,
1034
1100
  )
1035
1101
  case LLMResponseType.REASONING_ONLY | LLMResponseType.EMPTY:
1036
1102
  self._handle_no_content_response(
@@ -1041,6 +1107,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1041
1107
  on_event,
1042
1108
  stream,
1043
1109
  response_type=response_type,
1110
+ mask_secrets=False,
1044
1111
  )
1045
1112
 
1046
1113
  def _requires_user_confirmation(
@@ -253,9 +253,18 @@ class ResponseDispatchMixin:
253
253
  state: ConversationState,
254
254
  on_event: ConversationCallbackType,
255
255
  stream: StreamContext | None = None,
256
+ *,
257
+ mask_secrets: bool = True,
256
258
  ) -> None:
257
259
  """Handle LLM response with text content — finishes conversation."""
258
- self._emit_message_event(message, llm_response, conversation, on_event, stream)
260
+ self._emit_message_event(
261
+ message,
262
+ llm_response,
263
+ conversation,
264
+ on_event,
265
+ stream,
266
+ mask_secrets=mask_secrets,
267
+ )
259
268
  self._maybe_emit_vllm_tokens(llm_response, on_event)
260
269
  logger.debug("LLM produced a message response - awaits user input")
261
270
  state.execution_status = ConversationExecutionStatus.FINISHED
@@ -270,6 +279,7 @@ class ResponseDispatchMixin:
270
279
  stream: StreamContext | None = None,
271
280
  *,
272
281
  response_type: LLMResponseType,
282
+ mask_secrets: bool = True,
273
283
  ) -> None:
274
284
  """Handle LLM response with no user-facing content.
275
285
 
@@ -279,7 +289,14 @@ class ResponseDispatchMixin:
279
289
  """
280
290
  if response_type is LLMResponseType.EMPTY:
281
291
  logger.warning("LLM produced empty response - continuing agent loop")
282
- self._emit_message_event(message, llm_response, conversation, on_event, stream)
292
+ self._emit_message_event(
293
+ message,
294
+ llm_response,
295
+ conversation,
296
+ on_event,
297
+ stream,
298
+ mask_secrets=mask_secrets,
299
+ )
283
300
  self._maybe_emit_vllm_tokens(llm_response, on_event)
284
301
  self._send_corrective_nudge(on_event)
285
302
 
@@ -290,6 +307,8 @@ class ResponseDispatchMixin:
290
307
  conversation: LocalConversation,
291
308
  on_event: ConversationCallbackType,
292
309
  stream: StreamContext | None = None,
310
+ *,
311
+ mask_secrets: bool = True,
293
312
  ) -> MessageEvent:
294
313
  """Create and emit a MessageEvent, running critic if configured.
295
314
 
@@ -302,7 +321,9 @@ class ResponseDispatchMixin:
302
321
  msg_event = MessageEvent(
303
322
  **minted,
304
323
  source="agent",
305
- llm_message=self._mask_secrets(message, conversation),
324
+ llm_message=self._mask_secrets(message, conversation)
325
+ if mask_secrets
326
+ else message,
306
327
  llm_response_id=llm_response.id,
307
328
  )
308
329
  if self.critic is not None and self.critic.mode == "finish_and_message":
@@ -18,6 +18,7 @@ from openhands.sdk.context.prompts import render_template
18
18
  from openhands.sdk.context.view import View
19
19
  from openhands.sdk.event.base import LLMConvertibleEvent
20
20
  from openhands.sdk.event.condenser import Condensation
21
+ from openhands.sdk.event.llm_convertible import SystemPromptEvent
21
22
  from openhands.sdk.llm import LLM, Message, TextContent
22
23
  from openhands.sdk.logger import get_logger
23
24
  from openhands.sdk.observability.laminar import observe
@@ -27,6 +28,21 @@ from openhands.sdk.utils import maybe_truncate
27
28
  logger = get_logger(__name__)
28
29
 
29
30
 
31
+ def _leading_system_prompt_index(events: Sequence[LLMConvertibleEvent]) -> int | None:
32
+ """Index of the first ``SystemPromptEvent`` in ``events``, or ``None``.
33
+
34
+ The agent loop guarantees the ``SystemPromptEvent`` sits at the head of the
35
+ view (index 0), so condensation must never forget it -- otherwise the
36
+ condensed view would open with a non-system message and violate the
37
+ repo-wide "system before first user" invariant. Returns ``None`` when no
38
+ system prompt is present (e.g. unit-test views built without one).
39
+ """
40
+ for i, event in enumerate(events):
41
+ if isinstance(event, SystemPromptEvent):
42
+ return i
43
+ return None
44
+
45
+
30
46
  class Reason(Enum):
31
47
  """Reasons for condensation."""
32
48
 
@@ -186,6 +202,25 @@ class LLMSummarizingCondenser(RollingCondenser):
186
202
  if Reason.REQUEST in reasons:
187
203
  return CondensationRequirement.HARD
188
204
 
205
+ def _build_summary_messages(self, event_strings: Sequence[str]) -> list[Message]:
206
+ """Build the messages sent to the summarization LLM.
207
+
208
+ The summarization instructions are sent as a ``system`` message and the
209
+ events to summarize as a ``user`` message. Splitting roles keeps the
210
+ steering instructions in the provider's ``instructions``/``system`` slot
211
+ and the event payload in the ``input``/``user`` slot, which is the
212
+ canonical shape for both the Chat Completions and Responses APIs.
213
+ """
214
+ prompt_dir = os.path.join(os.path.dirname(__file__), "prompts")
215
+ system_prompt = render_template(prompt_dir, "summarizing_system.j2")
216
+ events_prompt = render_template(
217
+ prompt_dir, "summarizing_events.j2", events=event_strings
218
+ )
219
+ return [
220
+ Message(role="system", content=[TextContent(text=system_prompt)]),
221
+ Message(role="user", content=[TextContent(text=events_prompt)]),
222
+ ]
223
+
189
224
  def _generate_condensation(
190
225
  self,
191
226
  forgotten_events: Sequence[LLMConvertibleEvent],
@@ -215,13 +250,7 @@ class LLMSummarizingCondenser(RollingCondenser):
215
250
  for forgotten_event in forgotten_events
216
251
  ]
217
252
 
218
- prompt = render_template(
219
- os.path.join(os.path.dirname(__file__), "prompts"),
220
- "summarizing_prompt.j2",
221
- events=event_strings,
222
- )
223
-
224
- messages = [Message(role="user", content=[TextContent(text=prompt)])]
253
+ messages = self._build_summary_messages(event_strings)
225
254
 
226
255
  # Do not pass extra_body explicitly. The LLM handles forwarding
227
256
  # litellm_extra_body only when it is non-empty.
@@ -302,8 +331,16 @@ class LLMSummarizingCondenser(RollingCondenser):
302
331
  # Calculate naive forgetting end (without considering atomic boundaries)
303
332
  naive_end = len(view) - events_from_tail
304
333
 
305
- # Find actual forgetting_start: smallest manipulation index >= keep_first
306
- forgetting_start = view.manipulation_indices.find_next(self.keep_first)
334
+ # The leading SystemPromptEvent must never be forgotten, otherwise the
335
+ # condensed view would open with a non-system message. Floor the start
336
+ # of the forgetting range just past it (this also covers keep_first=0).
337
+ protected_prefix = self.keep_first
338
+ system_idx = _leading_system_prompt_index(view.events)
339
+ if system_idx is not None:
340
+ protected_prefix = max(protected_prefix, system_idx + 1)
341
+
342
+ # Find actual forgetting_start: smallest manipulation index >= protected_prefix
343
+ forgetting_start = view.manipulation_indices.find_next(protected_prefix)
307
344
 
308
345
  # Find actual forgetting_end: smallest manipulation index >= naive_end
309
346
  forgetting_end = view.manipulation_indices.find_next(naive_end)
@@ -326,14 +363,22 @@ class LLMSummarizingCondenser(RollingCondenser):
326
363
  the view is too large for the summarizing LLM to handle). In that case, we keep
327
364
  trimming down the contents until a summary can be generated.
328
365
  """
366
+ # Preserve the leading SystemPromptEvent: it must stay at the head of the
367
+ # view so the request still opens with a system message. Summarize the
368
+ # events *after* it and insert the summary right behind it.
369
+ system_idx = _leading_system_prompt_index(view.events)
370
+ preserve = (system_idx + 1) if system_idx is not None else 0
371
+ forgotten_events = view.events[preserve:]
372
+ summary_offset = preserve
373
+
329
374
  max_event_str_length: int | None = None
330
375
  attempts_remaining: int = self.hard_context_reset_max_retries
331
376
 
332
377
  while attempts_remaining > 0:
333
378
  try:
334
379
  return self._generate_condensation(
335
- forgotten_events=view.events,
336
- summary_offset=0,
380
+ forgotten_events=forgotten_events,
381
+ summary_offset=summary_offset,
337
382
  max_event_str_length=max_event_str_length,
338
383
  )
339
384
  except Exception as e:
@@ -411,13 +456,7 @@ class LLMSummarizingCondenser(RollingCondenser):
411
456
  for fe in forgotten_events
412
457
  ]
413
458
 
414
- prompt = render_template(
415
- os.path.join(os.path.dirname(__file__), "prompts"),
416
- "summarizing_prompt.j2",
417
- events=event_strings,
418
- )
419
-
420
- messages = [Message(role="user", content=[TextContent(text=prompt)])]
459
+ messages = self._build_summary_messages(event_strings)
421
460
 
422
461
  try:
423
462
  llm_response = await self.llm.agenerate(messages=messages, store=False)
@@ -477,14 +516,20 @@ class LLMSummarizingCondenser(RollingCondenser):
477
516
  agent_llm: LLM | None = None, # noqa: ARG002
478
517
  ) -> Condensation | None:
479
518
  """Async variant of :meth:`hard_context_reset`."""
519
+ # Preserve the leading SystemPromptEvent (see hard_context_reset).
520
+ system_idx = _leading_system_prompt_index(view.events)
521
+ preserve = (system_idx + 1) if system_idx is not None else 0
522
+ forgotten_events = view.events[preserve:]
523
+ summary_offset = preserve
524
+
480
525
  max_event_str_length: int | None = None
481
526
  attempts_remaining: int = self.hard_context_reset_max_retries
482
527
 
483
528
  while attempts_remaining > 0:
484
529
  try:
485
530
  return await self._agenerate_condensation(
486
- forgotten_events=view.events,
487
- summary_offset=0,
531
+ forgotten_events=forgotten_events,
532
+ summary_offset=summary_offset,
488
533
  max_event_str_length=max_event_str_length,
489
534
  )
490
535
  except Exception as e:
@@ -0,0 +1,7 @@
1
+ {% for event in events %}
2
+ <EVENT>
3
+ {{ event }}
4
+ </EVENT>
5
+ {% endfor %}
6
+
7
+ Now summarize the events using the rules above.
@@ -45,11 +45,3 @@ USER_CONTEXT: Write 20 haikus based on coin flip results
45
45
  COMPLETED: 15 haikus written for results [T,H,T,H,T,H,T,T,H,T,H,T,H,T,H]
46
46
  PENDING: 5 more haikus needed
47
47
  CURRENT_STATE: Last flip: Heads, Haiku count: 15/20
48
-
49
- {% for event in events %}
50
- <EVENT>
51
- {{ event }}
52
- </EVENT>
53
- {% endfor %}
54
-
55
- Now summarize the events using the rules above.
@@ -14,7 +14,10 @@ from typing import Any
14
14
 
15
15
  from pydantic import BaseModel, Field
16
16
 
17
- from openhands.sdk.conversation.goal.prompts import JUDGE_PROMPT
17
+ from openhands.sdk.conversation.goal.prompts import (
18
+ JUDGE_SYSTEM_PROMPT,
19
+ JUDGE_USER_PROMPT,
20
+ )
18
21
  from openhands.sdk.event import Event, LLMConvertibleEvent
19
22
  from openhands.sdk.llm import LLM, Message, TextContent, content_to_str
20
23
  from openhands.sdk.logger import get_logger
@@ -55,15 +58,21 @@ def judge_goal(judge_llm: LLM, objective: str, events: Sequence[Event]) -> GoalV
55
58
  """
56
59
  convertible = [e for e in events if isinstance(e, LLMConvertibleEvent)]
57
60
  transcript = _render_transcript(convertible)
58
- prompt = JUDGE_PROMPT.format(objective=objective, transcript=transcript)
61
+ system_prompt = JUDGE_SYSTEM_PROMPT
62
+ user_prompt = JUDGE_USER_PROMPT.format(objective=objective, transcript=transcript)
59
63
 
60
64
  # The judge only needs the verdict text. Force non-streaming so reusing a
61
65
  # streaming agent LLM as the judge does not trip completion()'s requirement
62
66
  # of an on_token callback when stream=True.
63
67
  if judge_llm.stream:
64
68
  judge_llm = judge_llm.model_copy(update={"stream": False})
69
+ # Build a canonical system + user pair: steering/format instructions live in
70
+ # the system message, the objective + transcript payload in the user message.
65
71
  response = judge_llm.completion(
66
- messages=[Message(role="user", content=[TextContent(text=prompt)])]
72
+ messages=[
73
+ Message(role="system", content=[TextContent(text=system_prompt)]),
74
+ Message(role="user", content=[TextContent(text=user_prompt)]),
75
+ ]
67
76
  )
68
77
  verdict = _parse_verdict(response.message)
69
78
  logger.debug("judge_goal verdict: %s", verdict)
@@ -3,26 +3,44 @@
3
3
  from typing import Final
4
4
 
5
5
 
6
- JUDGE_PROMPT: Final[str] = """You are auditing whether a long-running GOAL has \
7
- been COMPLETED by an AI software agent.
8
-
9
- <objective>
10
- {objective}
11
- </objective>
6
+ JUDGE_SYSTEM_PROMPT: Final[str] = """\
7
+ You are auditing whether a long-running GOAL has been COMPLETED by an AI \
8
+ software agent.
12
9
 
13
10
  Derive the concrete requirements implied by the objective. For EACH requirement,
14
- look for authoritative evidence in the transcript below: file contents, command
11
+ look for authoritative evidence in the transcript: file contents, command
15
12
  output, or test results produced by the agent. Treat missing, uncertain, or
16
13
  merely-claimed-but-unverified evidence as NOT satisfied.
17
14
 
15
+ Respond with STRICT JSONand nothing else, in exactly this shape:
16
+ {"score": <float 0.0-1.0, probability the FULL objective is provably done>, \
17
+ "complete": <true|false>, "missing": "<concise description of what remains, or \
18
+ an empty string if complete>"}"""
19
+
20
+
21
+ JUDGE_USER_PROMPT: Final[str] = """\
22
+ <objective>
23
+ {objective}
24
+ </objective>
25
+
18
26
  <transcript>
19
27
  {transcript}
20
- </transcript>
21
-
22
- Respond with STRICT JSON and nothing else, in exactly this shape:
23
- {{"score": <float 0.0-1.0, probability the FULL objective is provably done>, \
24
- "complete": <true|false>, "missing": "<concise description of what remains, or \
25
- an empty string if complete>"}}"""
28
+ </transcript>"""
29
+
30
+
31
+ JUDGE_PROMPT: Final[str] = (
32
+ JUDGE_SYSTEM_PROMPT.replace("{", "{{").replace("}", "}}")
33
+ + "\n\n"
34
+ + JUDGE_USER_PROMPT
35
+ )
36
+ """Backwards-compatible concatenation of the system steering prompt and the user
37
+ payload template. Kept so external callers that still format a single prompt
38
+ continue to work; new code should use ``JUDGE_SYSTEM_PROMPT`` and
39
+ ``JUDGE_USER_PROMPT`` separately to build a ``system + user`` message pair.
40
+
41
+ JUDGE_SYSTEM_PROMPT carries the raw, un-escaped JSON shape because it is sent
42
+ unformatted ins ``judge_goal``; ``JUDGE_PROMPT`` re-escapes it so the combined
43
+ template survives ``.format(objective=..., transcript=...)``."""
26
44
 
27
45
 
28
46
  FOLLOWUP_PROMPT: Final[str] = """The goal is NOT yet complete (audit iteration \