openhands-sdk 1.45.0__tar.gz → 1.47.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 (303) hide show
  1. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/PKG-INFO +1 -1
  2. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/agent/acp_agent.py +50 -0
  3. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/agent/agent.py +48 -8
  4. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/agent/response_dispatch.py +29 -4
  5. openhands_sdk-1.47.0/openhands/sdk/agent/stream_context.py +292 -0
  6. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/impl/local_conversation.py +22 -0
  7. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/secret_registry.py +66 -0
  8. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/extensions/fetch.py +8 -9
  9. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/model_info.py +79 -0
  10. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/telemetry.py +92 -14
  11. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/observability/laminar.py +28 -0
  12. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/models.py +5 -0
  13. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands_sdk.egg-info/PKG-INFO +1 -1
  14. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands_sdk.egg-info/SOURCES.txt +2 -0
  15. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/pyproject.toml +1 -1
  16. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/__init__.py +0 -0
  17. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/agent/__init__.py +0 -0
  18. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/agent/acp_file_credentials.py +0 -0
  19. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/agent/acp_models.py +0 -0
  20. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/agent/acp_tracing.py +0 -0
  21. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/agent/base.py +0 -0
  22. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/agent/critic_mixin.py +0 -0
  23. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/agent/parallel_executor.py +0 -0
  24. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/agent/utils.py +0 -0
  25. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/banner.py +0 -0
  26. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/__init__.py +0 -0
  27. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/agent_context.py +0 -0
  28. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/condenser/__init__.py +0 -0
  29. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/condenser/base.py +0 -0
  30. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +0 -0
  31. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
  32. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
  33. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 +0 -0
  34. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/condenser/utils.py +0 -0
  35. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/memory.py +0 -0
  36. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/prompts/__init__.py +0 -0
  37. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/prompts/presets.py +0 -0
  38. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/prompts/prompt.py +0 -0
  39. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/prompts/registry.py +0 -0
  40. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/prompts/section.py +0 -0
  41. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
  42. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
  43. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/prompts/sections/planning.py +0 -0
  44. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/prompts/sections/static.py +0 -0
  45. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
  46. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
  47. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/view/__init__.py +0 -0
  48. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/view/manipulation_indices.py +0 -0
  49. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/view/properties/__init__.py +0 -0
  50. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/view/properties/base.py +0 -0
  51. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
  52. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
  53. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
  54. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
  55. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/context/view/view.py +0 -0
  56. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/__init__.py +0 -0
  57. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/base.py +0 -0
  58. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/cancellation.py +0 -0
  59. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/conversation.py +0 -0
  60. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/conversation_stats.py +0 -0
  61. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/event_store.py +0 -0
  62. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/events_list_base.py +0 -0
  63. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/exceptions.py +0 -0
  64. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/fifo_lock.py +0 -0
  65. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/goal/__init__.py +0 -0
  66. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/goal/controller.py +0 -0
  67. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/goal/judge.py +0 -0
  68. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/goal/prompts.py +0 -0
  69. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/goal/runner.py +0 -0
  70. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/impl/__init__.py +0 -0
  71. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/impl/remote_conversation.py +0 -0
  72. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/persistence_const.py +0 -0
  73. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/request.py +0 -0
  74. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
  75. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/response_utils.py +0 -0
  76. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/state.py +0 -0
  77. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/stuck_detector.py +0 -0
  78. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/title_utils.py +0 -0
  79. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/types.py +0 -0
  80. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
  81. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/visualizer/base.py +0 -0
  82. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/conversation/visualizer/default.py +0 -0
  83. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/credential.py +0 -0
  84. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/__init__.py +0 -0
  85. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/base.py +0 -0
  86. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/impl/__init__.py +0 -0
  87. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/impl/agent_finished.py +0 -0
  88. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/impl/api/__init__.py +0 -0
  89. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
  90. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/impl/api/client.py +0 -0
  91. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/impl/api/critic.py +0 -0
  92. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
  93. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/impl/empty_patch.py +0 -0
  94. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/impl/pass_critic.py +0 -0
  95. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/critic/result.py +0 -0
  96. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/__init__.py +0 -0
  97. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/acp_tool_call.py +0 -0
  98. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/base.py +0 -0
  99. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/condenser.py +0 -0
  100. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/conversation_error.py +0 -0
  101. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/conversation_state.py +0 -0
  102. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/error_classification.py +0 -0
  103. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/hook_execution.py +0 -0
  104. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/llm_completion_log.py +0 -0
  105. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
  106. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/llm_convertible/action.py +0 -0
  107. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/llm_convertible/message.py +0 -0
  108. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/llm_convertible/observation.py +0 -0
  109. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
  110. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/llm_convertible/system.py +0 -0
  111. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/resume_transcript.py +0 -0
  112. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/streaming_delta.py +0 -0
  113. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/token.py +0 -0
  114. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/types.py +0 -0
  115. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/event/user_action.py +0 -0
  116. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/extensions/__init__.py +0 -0
  117. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/extensions/installation/__init__.py +0 -0
  118. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/extensions/installation/info.py +0 -0
  119. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/extensions/installation/interface.py +0 -0
  120. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/extensions/installation/manager.py +0 -0
  121. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/extensions/installation/metadata.py +0 -0
  122. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/extensions/installation/utils.py +0 -0
  123. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/git/cached_repo.py +0 -0
  124. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/git/exceptions.py +0 -0
  125. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/git/git_changes.py +0 -0
  126. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/git/git_commits.py +0 -0
  127. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/git/git_diff.py +0 -0
  128. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/git/models.py +0 -0
  129. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/git/utils.py +0 -0
  130. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/hooks/__init__.py +0 -0
  131. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/hooks/config.py +0 -0
  132. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/hooks/conversation_hooks.py +0 -0
  133. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/hooks/executor.py +0 -0
  134. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/hooks/manager.py +0 -0
  135. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/hooks/types.py +0 -0
  136. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/io/__init__.py +0 -0
  137. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/io/base.py +0 -0
  138. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/io/cache.py +0 -0
  139. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/io/local.py +0 -0
  140. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/io/memory.py +0 -0
  141. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/__init__.py +0 -0
  142. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/auth/__init__.py +0 -0
  143. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/auth/credentials.py +0 -0
  144. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/auth/openai.py +0 -0
  145. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/cleanup_profile.py +0 -0
  146. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/exceptions/__init__.py +0 -0
  147. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/exceptions/classifier.py +0 -0
  148. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/exceptions/mapping.py +0 -0
  149. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/exceptions/types.py +0 -0
  150. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/fallback_strategy.py +0 -0
  151. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/llm.py +0 -0
  152. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/llm_profile_store.py +0 -0
  153. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/llm_registry.py +0 -0
  154. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/llm_response.py +0 -0
  155. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/message.py +0 -0
  156. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
  157. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
  158. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
  159. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/options/__init__.py +0 -0
  160. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/options/chat_options.py +0 -0
  161. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/options/common.py +0 -0
  162. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/options/responses_options.py +0 -0
  163. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/provider_connection_store.py +0 -0
  164. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/router/__init__.py +0 -0
  165. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/router/base.py +0 -0
  166. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
  167. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/router/impl/random.py +0 -0
  168. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/streaming.py +0 -0
  169. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/image_inline.py +0 -0
  170. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/image_resize.py +0 -0
  171. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
  172. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/metrics.py +0 -0
  173. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/model_features.py +0 -0
  174. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
  175. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
  176. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
  177. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
  178. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
  179. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
  180. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
  181. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/unverified_models.py +0 -0
  182. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/verified_models.py +0 -0
  183. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
  184. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/logger/__init__.py +0 -0
  185. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/logger/logger.py +0 -0
  186. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/logger/rolling.py +0 -0
  187. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/marketplace/__init__.py +0 -0
  188. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/marketplace/registration.py +0 -0
  189. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/marketplace/registry.py +0 -0
  190. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/marketplace/types.py +0 -0
  191. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/mcp/__init__.py +0 -0
  192. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/mcp/client.py +0 -0
  193. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/mcp/config.py +0 -0
  194. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/mcp/definition.py +0 -0
  195. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/mcp/exceptions.py +0 -0
  196. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/mcp/tool.py +0 -0
  197. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/mcp/utils.py +0 -0
  198. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/observability/__init__.py +0 -0
  199. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/observability/utils.py +0 -0
  200. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/__init__.py +0 -0
  201. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/discovery.py +0 -0
  202. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/fetch.py +0 -0
  203. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/format/__init__.py +0 -0
  204. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/format/agent_plugins.py +0 -0
  205. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/format/base.py +0 -0
  206. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/format/claude_code.py +0 -0
  207. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
  208. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/installed.py +0 -0
  209. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/loader.py +0 -0
  210. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/plugin.py +0 -0
  211. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/source.py +0 -0
  212. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/plugin/types.py +0 -0
  213. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/profiles/__init__.py +0 -0
  214. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/profiles/agent_profile.py +0 -0
  215. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/profiles/agent_profile_store.py +0 -0
  216. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/profiles/profile_refs.py +0 -0
  217. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/profiles/resolver.py +0 -0
  218. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/profiles/seed.py +0 -0
  219. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/py.typed +0 -0
  220. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/secret/__init__.py +0 -0
  221. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/secret/secrets.py +0 -0
  222. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/__init__.py +0 -0
  223. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/_shell_ast.py +0 -0
  224. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/analyzer.py +0 -0
  225. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/confirmation_policy.py +0 -0
  226. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
  227. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
  228. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
  229. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -0
  230. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
  231. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/ensemble.py +0 -0
  232. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/grayswan/__init__.py +0 -0
  233. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/grayswan/analyzer.py +0 -0
  234. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/grayswan/utils.py +0 -0
  235. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/llm_analyzer.py +0 -0
  236. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/risk.py +0 -0
  237. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/shell_parser.py +0 -0
  238. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/toolshield_helpers.py +0 -0
  239. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
  240. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/settings/__init__.py +0 -0
  241. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/settings/acp_install_catalog.py +0 -0
  242. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/settings/acp_providers.py +0 -0
  243. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/settings/api_models.py +0 -0
  244. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/settings/metadata.py +0 -0
  245. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/settings/model.py +0 -0
  246. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/skills/__init__.py +0 -0
  247. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/skills/exceptions.py +0 -0
  248. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/skills/execute.py +0 -0
  249. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/skills/fetch.py +0 -0
  250. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/skills/installed.py +0 -0
  251. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/skills/skill.py +0 -0
  252. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/skills/trigger.py +0 -0
  253. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/skills/types.py +0 -0
  254. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/skills/utils.py +0 -0
  255. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/subagent/__init__.py +0 -0
  256. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/subagent/load.py +0 -0
  257. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/subagent/registry.py +0 -0
  258. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/subagent/schema.py +0 -0
  259. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/testing/__init__.py +0 -0
  260. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/testing/test_llm.py +0 -0
  261. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/__init__.py +0 -0
  262. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/builtins/__init__.py +0 -0
  263. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/builtins/finish.py +0 -0
  264. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
  265. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
  266. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/builtins/think.py +0 -0
  267. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
  268. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/client_tool.py +0 -0
  269. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/defaults.py +0 -0
  270. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/registry.py +0 -0
  271. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/schema.py +0 -0
  272. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/spec.py +0 -0
  273. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/tool/tool.py +0 -0
  274. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/__init__.py +0 -0
  275. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/async_executor.py +0 -0
  276. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/async_utils.py +0 -0
  277. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/cipher.py +0 -0
  278. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/command.py +0 -0
  279. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/datetime.py +0 -0
  280. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/deprecation.py +0 -0
  281. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/files.py +0 -0
  282. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/github.py +0 -0
  283. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/json.py +0 -0
  284. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/paging.py +0 -0
  285. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/path.py +0 -0
  286. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/pydantic_secrets.py +0 -0
  287. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/redact.py +0 -0
  288. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/truncate.py +0 -0
  289. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/utils/visualize.py +0 -0
  290. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/workspace/__init__.py +0 -0
  291. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/workspace/base.py +0 -0
  292. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/workspace/local.py +0 -0
  293. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/workspace/models.py +0 -0
  294. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/workspace/remote/__init__.py +0 -0
  295. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
  296. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/workspace/remote/base.py +0 -0
  297. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
  298. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/workspace/repo.py +0 -0
  299. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands/sdk/workspace/workspace.py +0 -0
  300. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands_sdk.egg-info/dependency_links.txt +0 -0
  301. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands_sdk.egg-info/requires.txt +0 -0
  302. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/openhands_sdk.egg-info/top_level.txt +0 -0
  303. {openhands_sdk-1.45.0 → openhands_sdk-1.47.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-sdk
3
- Version: 1.45.0
3
+ Version: 1.47.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
@@ -82,6 +82,7 @@ from openhands.sdk.agent.acp_file_credentials import (
82
82
  from openhands.sdk.agent.acp_models import ACPModelInfo
83
83
  from openhands.sdk.agent.acp_tracing import ACPTurnTrace
84
84
  from openhands.sdk.agent.base import AgentBase
85
+ from openhands.sdk.agent.stream_context import StreamContext
85
86
  from openhands.sdk.context import AgentContext
86
87
  from openhands.sdk.conversation.state import ConversationExecutionStatus
87
88
  from openhands.sdk.credential import (
@@ -1926,6 +1927,10 @@ class ACPAgent(AgentBase):
1926
1927
  _suffix_install_state: str = PrivateAttr(default="unused")
1927
1928
  _installed_suffix: str | None = PrivateAttr(default=None)
1928
1929
  _restart_session_on_next_turn: bool = PrivateAttr(default=False)
1930
+ # Stream identity for the turn in flight; see stream_context.py. Held on
1931
+ # the agent rather than threaded through the finalizers because the ACP
1932
+ # turn already resolves through four of them.
1933
+ _stream: StreamContext | None = PrivateAttr(default=None)
1929
1934
  _resumed_existing_session: bool = PrivateAttr(default=False)
1930
1935
  _file_credential_lifecycles: dict[str, ACPFileCredentialLifecycle] = PrivateAttr(
1931
1936
  default_factory=dict
@@ -3688,7 +3693,13 @@ class ACPAgent(AgentBase):
3688
3693
  # completed turn for eval/remote consumers, matching #2190.
3689
3694
  finish_action = FinishAction(message=response_text)
3690
3695
  tc_id = str(uuid.uuid4())
3696
+ # An ACP turn's streamed text lands here, not in a MessageEvent, so
3697
+ # this is the event that retires the stream's slot.
3698
+ minted: dict[str, Any] = {}
3699
+ if self._stream is not None and (item_id := self._stream.claim()):
3700
+ minted["id"] = item_id
3691
3701
  action_event = ActionEvent(
3702
+ **minted,
3692
3703
  source="agent",
3693
3704
  thought=[],
3694
3705
  reasoning_content=thought_text or None,
@@ -3704,6 +3715,8 @@ class ACPAgent(AgentBase):
3704
3715
  llm_response_id=str(uuid.uuid4()),
3705
3716
  )
3706
3717
  on_event(action_event)
3718
+ if self._stream is not None and minted:
3719
+ self._stream.commit()
3707
3720
  on_event(
3708
3721
  ObservationEvent(
3709
3722
  observation=FinishObservation.from_text(text=response_text),
@@ -3865,6 +3878,19 @@ class ACPAgent(AgentBase):
3865
3878
  (``LocalConversation.arun``) goes through :meth:`astep`, which
3866
3879
  avoids the cross-thread state-lock deadlock described in #3348.
3867
3880
  """
3881
+ with StreamContext.open(conversation, on_token) as stream:
3882
+ self._stream = stream
3883
+ try:
3884
+ self._step(conversation, on_event, stream.token_callback)
3885
+ finally:
3886
+ self._stream = None
3887
+
3888
+ def _step(
3889
+ self,
3890
+ conversation: LocalConversation,
3891
+ on_event: ConversationCallbackType,
3892
+ on_token: ConversationTokenCallbackType | None = None,
3893
+ ) -> None:
3868
3894
  state = conversation.state
3869
3895
 
3870
3896
  if self._restart_session_on_next_turn:
@@ -3933,6 +3959,8 @@ class ACPAgent(AgentBase):
3933
3959
  )
3934
3960
  time.sleep(delay)
3935
3961
  self._cancel_inflight_tool_calls()
3962
+ if self._stream is not None:
3963
+ self._stream.new_attempt()
3936
3964
  self._reset_client_for_turn(
3937
3965
  on_token,
3938
3966
  on_event,
@@ -3963,6 +3991,8 @@ class ACPAgent(AgentBase):
3963
3991
  )
3964
3992
  time.sleep(delay)
3965
3993
  self._cancel_inflight_tool_calls()
3994
+ if self._stream is not None:
3995
+ self._stream.new_attempt()
3966
3996
  self._reset_client_for_turn(
3967
3997
  on_token,
3968
3998
  on_event,
@@ -4023,6 +4053,22 @@ class ACPAgent(AgentBase):
4023
4053
  supplied by ``LocalConversation.arun`` is responsible for taking
4024
4054
  the state lock around each individual event.
4025
4055
  """
4056
+ with StreamContext.open(conversation, on_token) as stream:
4057
+ self._stream = stream
4058
+ try:
4059
+ await self._astep(
4060
+ conversation, on_event, stream.token_callback, prompt_message
4061
+ )
4062
+ finally:
4063
+ self._stream = None
4064
+
4065
+ async def _astep(
4066
+ self,
4067
+ conversation: LocalConversation,
4068
+ on_event: ConversationCallbackType,
4069
+ on_token: ConversationTokenCallbackType | None = None,
4070
+ prompt_message: MessageEvent | None = None,
4071
+ ) -> None:
4026
4072
  state = conversation.state
4027
4073
 
4028
4074
  if self._restart_session_on_next_turn:
@@ -4099,6 +4145,8 @@ class ACPAgent(AgentBase):
4099
4145
  )
4100
4146
  await asyncio.sleep(delay)
4101
4147
  self._cancel_inflight_tool_calls()
4148
+ if self._stream is not None:
4149
+ self._stream.new_attempt()
4102
4150
  self._reset_client_for_turn(
4103
4151
  on_token,
4104
4152
  on_event,
@@ -4126,6 +4174,8 @@ class ACPAgent(AgentBase):
4126
4174
  )
4127
4175
  await asyncio.sleep(delay)
4128
4176
  self._cancel_inflight_tool_calls()
4177
+ if self._stream is not None:
4178
+ self._stream.new_attempt()
4129
4179
  self._reset_client_for_turn(
4130
4180
  on_token,
4131
4181
  on_event,
@@ -4,7 +4,7 @@ import json
4
4
  import re
5
5
  from collections.abc import Callable
6
6
  from dataclasses import dataclass, field
7
- from typing import TYPE_CHECKING
7
+ from typing import TYPE_CHECKING, Any
8
8
 
9
9
  from pydantic import PrivateAttr, ValidationError, model_validator
10
10
 
@@ -18,6 +18,7 @@ from openhands.sdk.agent.response_dispatch import (
18
18
  ResponseDispatchMixin,
19
19
  classify_response,
20
20
  )
21
+ from openhands.sdk.agent.stream_context import StreamContext
21
22
  from openhands.sdk.agent.utils import (
22
23
  amake_llm_completion,
23
24
  aprepare_llm_messages,
@@ -639,6 +640,15 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
639
640
  conversation: LocalConversation,
640
641
  on_event: ConversationCallbackType,
641
642
  on_token: ConversationTokenCallbackType | None = None,
643
+ ) -> None:
644
+ with StreamContext.open(conversation, on_token) as stream:
645
+ self._step(conversation, on_event, stream)
646
+
647
+ def _step(
648
+ self,
649
+ conversation: LocalConversation,
650
+ on_event: ConversationCallbackType,
651
+ stream: StreamContext,
642
652
  ) -> None:
643
653
  state = conversation.state
644
654
  # Check for pending actions (implicit confirmation)
@@ -721,7 +731,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
721
731
  self.llm,
722
732
  _messages,
723
733
  tools=list(self.tools_map.values()),
724
- on_token=on_token,
734
+ on_token=stream.token_callback,
725
735
  call_context=call_context,
726
736
  )
727
737
  except FunctionCallValidationError as e:
@@ -807,11 +817,11 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
807
817
  match response_type:
808
818
  case LLMResponseType.TOOL_CALLS:
809
819
  self._handle_tool_calls(
810
- message, llm_response, conversation, state, on_event
820
+ message, llm_response, conversation, state, on_event, stream
811
821
  )
812
822
  case LLMResponseType.CONTENT:
813
823
  self._handle_content_response(
814
- message, llm_response, conversation, state, on_event
824
+ message, llm_response, conversation, state, on_event, stream
815
825
  )
816
826
  case LLMResponseType.REASONING_ONLY | LLMResponseType.EMPTY:
817
827
  self._handle_no_content_response(
@@ -820,6 +830,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
820
830
  conversation,
821
831
  state,
822
832
  on_event,
833
+ stream,
823
834
  response_type=response_type,
824
835
  )
825
836
 
@@ -839,6 +850,15 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
839
850
  parallel calls with :func:`asyncio.gather`, keeping the event
840
851
  loop responsive during blocking tool I/O.
841
852
  """
853
+ with StreamContext.open(conversation, on_token) as stream:
854
+ await self._astep(conversation, on_event, stream)
855
+
856
+ async def _astep(
857
+ self,
858
+ conversation: LocalConversation,
859
+ on_event: ConversationCallbackType,
860
+ stream: StreamContext,
861
+ ) -> None:
842
862
  state = conversation.state
843
863
  # Check for pending actions (implicit confirmation)
844
864
  pending_actions = ConversationState.get_unmatched_actions(state.active_branch())
@@ -918,7 +938,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
918
938
  self.llm,
919
939
  _messages,
920
940
  tools=list(self.tools_map.values()),
921
- on_token=on_token,
941
+ on_token=stream.token_callback,
922
942
  call_context=call_context,
923
943
  )
924
944
  except FunctionCallValidationError as e:
@@ -1006,11 +1026,11 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1006
1026
  match response_type:
1007
1027
  case LLMResponseType.TOOL_CALLS:
1008
1028
  await self._ahandle_tool_calls(
1009
- message, llm_response, conversation, state, on_event
1029
+ message, llm_response, conversation, state, on_event, stream
1010
1030
  )
1011
1031
  case LLMResponseType.CONTENT:
1012
1032
  self._handle_content_response(
1013
- message, llm_response, conversation, state, on_event
1033
+ message, llm_response, conversation, state, on_event, stream
1014
1034
  )
1015
1035
  case LLMResponseType.REASONING_ONLY | LLMResponseType.EMPTY:
1016
1036
  self._handle_no_content_response(
@@ -1019,6 +1039,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1019
1039
  conversation,
1020
1040
  state,
1021
1041
  on_event,
1042
+ stream,
1022
1043
  response_type=response_type,
1023
1044
  )
1024
1045
 
@@ -1153,6 +1174,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1153
1174
  reasoning_content: str | None = None,
1154
1175
  thinking_blocks: list[ThinkingBlock | RedactedThinkingBlock] | None = None,
1155
1176
  responses_reasoning_item: ReasoningItemModel | None = None,
1177
+ stream: StreamContext | None = None,
1156
1178
  ) -> None:
1157
1179
  try:
1158
1180
  json.loads(tool_call.arguments)
@@ -1168,7 +1190,11 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1168
1190
  }
1169
1191
  )
1170
1192
 
1193
+ minted: dict[str, Any] = {}
1194
+ if stream is not None and (item_id := stream.claim()):
1195
+ minted["id"] = item_id
1171
1196
  tc_event = ActionEvent(
1197
+ **minted,
1172
1198
  source="agent",
1173
1199
  thought=thought or [],
1174
1200
  reasoning_content=reasoning_content,
@@ -1181,6 +1207,8 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1181
1207
  action=None,
1182
1208
  )
1183
1209
  on_event(tc_event)
1210
+ if stream is not None and minted:
1211
+ stream.commit()
1184
1212
  error_event = AgentErrorEvent(
1185
1213
  error=error,
1186
1214
  tool_name=tool_name,
@@ -1207,6 +1235,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1207
1235
  reasoning_content: str | None = None,
1208
1236
  thinking_blocks: list[ThinkingBlock | RedactedThinkingBlock] | None = None,
1209
1237
  responses_reasoning_item: ReasoningItemModel | None = None,
1238
+ stream: StreamContext | None = None,
1210
1239
  ) -> ActionEvent | None:
1211
1240
  """Converts a tool call into an ActionEvent, validating arguments.
1212
1241
 
@@ -1249,6 +1278,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1249
1278
  reasoning_content=reasoning_content,
1250
1279
  thinking_blocks=thinking_blocks,
1251
1280
  responses_reasoning_item=responses_reasoning_item,
1281
+ stream=stream,
1252
1282
  )
1253
1283
  return
1254
1284
 
@@ -1311,11 +1341,19 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1311
1341
  reasoning_content=reasoning_content,
1312
1342
  thinking_blocks=thinking_blocks,
1313
1343
  responses_reasoning_item=responses_reasoning_item,
1344
+ stream=stream,
1314
1345
  )
1315
1346
  return
1316
1347
 
1317
- # Create initial action event
1348
+ # Create initial action event. Claimed here rather than at the call
1349
+ # site so an error path, which never builds this event, leaves the slot
1350
+ # open for the abort in StreamContext.__exit__.
1351
+ minted: dict[str, Any] = {}
1352
+ if stream is not None and (item_id := stream.claim()):
1353
+ minted["id"] = item_id
1354
+
1318
1355
  action_event = ActionEvent(
1356
+ **minted,
1319
1357
  action=action,
1320
1358
  thought=thought or [],
1321
1359
  reasoning_content=reasoning_content,
@@ -1339,6 +1377,8 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1339
1377
  )
1340
1378
 
1341
1379
  on_event(action_event)
1380
+ if stream is not None and minted:
1381
+ stream.commit()
1342
1382
  return action_event
1343
1383
 
1344
1384
  def _execute_action_event(
@@ -9,8 +9,9 @@ Contains:
9
9
  from __future__ import annotations
10
10
 
11
11
  from enum import StrEnum
12
- from typing import TYPE_CHECKING
12
+ from typing import TYPE_CHECKING, Any
13
13
 
14
+ from openhands.sdk.agent.stream_context import StreamContext
14
15
  from openhands.sdk.conversation.state import ConversationExecutionStatus
15
16
  from openhands.sdk.event import MessageEvent
16
17
  from openhands.sdk.llm import LLMResponse, Message, TextContent
@@ -106,6 +107,7 @@ class ResponseDispatchMixin:
106
107
  list[ThinkingBlock | RedactedThinkingBlock] | None
107
108
  ) = None,
108
109
  responses_reasoning_item: ReasoningItemModel | None = None,
110
+ stream: StreamContext | None = None,
109
111
  ) -> ActionEvent | None: ...
110
112
 
111
113
  def _execute_actions(
@@ -147,6 +149,7 @@ class ResponseDispatchMixin:
147
149
  conversation: LocalConversation,
148
150
  state: ConversationState,
149
151
  on_event: ConversationCallbackType,
152
+ stream: StreamContext | None = None,
150
153
  ) -> None:
151
154
  """Handle LLM response containing tool calls."""
152
155
  if not all(isinstance(c, TextContent) for c in message.content):
@@ -172,6 +175,9 @@ class ResponseDispatchMixin:
172
175
  responses_reasoning_item=(
173
176
  message.responses_reasoning_item if i == 0 else None
174
177
  ),
178
+ # The streamed text is this action's thought, so the first
179
+ # action event is what retires the slot.
180
+ stream=stream if i == 0 else None,
175
181
  )
176
182
  if action_event is None:
177
183
  continue
@@ -192,6 +198,7 @@ class ResponseDispatchMixin:
192
198
  conversation: LocalConversation,
193
199
  state: ConversationState,
194
200
  on_event: ConversationCallbackType,
201
+ stream: StreamContext | None = None,
195
202
  ) -> None:
196
203
  """Async variant of :meth:`_handle_tool_calls`.
197
204
 
@@ -222,6 +229,9 @@ class ResponseDispatchMixin:
222
229
  responses_reasoning_item=(
223
230
  message.responses_reasoning_item if i == 0 else None
224
231
  ),
232
+ # The streamed text is this action's thought, so the first
233
+ # action event is what retires the slot.
234
+ stream=stream if i == 0 else None,
225
235
  )
226
236
  if action_event is None:
227
237
  continue
@@ -242,9 +252,10 @@ class ResponseDispatchMixin:
242
252
  conversation: LocalConversation,
243
253
  state: ConversationState,
244
254
  on_event: ConversationCallbackType,
255
+ stream: StreamContext | None = None,
245
256
  ) -> None:
246
257
  """Handle LLM response with text content — finishes conversation."""
247
- self._emit_message_event(message, llm_response, conversation, on_event)
258
+ self._emit_message_event(message, llm_response, conversation, on_event, stream)
248
259
  self._maybe_emit_vllm_tokens(llm_response, on_event)
249
260
  logger.debug("LLM produced a message response - awaits user input")
250
261
  state.execution_status = ConversationExecutionStatus.FINISHED
@@ -256,6 +267,7 @@ class ResponseDispatchMixin:
256
267
  conversation: LocalConversation,
257
268
  state: ConversationState, # noqa: ARG002
258
269
  on_event: ConversationCallbackType,
270
+ stream: StreamContext | None = None,
259
271
  *,
260
272
  response_type: LLMResponseType,
261
273
  ) -> None:
@@ -267,7 +279,7 @@ class ResponseDispatchMixin:
267
279
  """
268
280
  if response_type is LLMResponseType.EMPTY:
269
281
  logger.warning("LLM produced empty response - continuing agent loop")
270
- self._emit_message_event(message, llm_response, conversation, on_event)
282
+ self._emit_message_event(message, llm_response, conversation, on_event, stream)
271
283
  self._maybe_emit_vllm_tokens(llm_response, on_event)
272
284
  self._send_corrective_nudge(on_event)
273
285
 
@@ -277,9 +289,18 @@ class ResponseDispatchMixin:
277
289
  llm_response: LLMResponse,
278
290
  conversation: LocalConversation,
279
291
  on_event: ConversationCallbackType,
292
+ stream: StreamContext | None = None,
280
293
  ) -> MessageEvent:
281
- """Create and emit a MessageEvent, running critic if configured."""
294
+ """Create and emit a MessageEvent, running critic if configured.
295
+
296
+ The message carries the id its own stream minted, so a client holding
297
+ an open slot retires it on ``frame.event.id == slot.item_id``.
298
+ """
299
+ minted: dict[str, Any] = {}
300
+ if stream is not None and (item_id := stream.claim()):
301
+ minted["id"] = item_id
282
302
  msg_event = MessageEvent(
303
+ **minted,
283
304
  source="agent",
284
305
  llm_message=self._mask_secrets(message, conversation),
285
306
  llm_response_id=llm_response.id,
@@ -291,6 +312,10 @@ class ResponseDispatchMixin:
291
312
  update={"critic_result": critic_result}
292
313
  )
293
314
  on_event(msg_event)
315
+ # Retired only now: on_event can raise while persisting, and a slot
316
+ # retired before that leaves the client holding it open forever.
317
+ if stream is not None and minted:
318
+ stream.commit()
294
319
  return msg_event
295
320
 
296
321
  @staticmethod