openhands-sdk 1.51.0__tar.gz → 1.53.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 (318) hide show
  1. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/PKG-INFO +2 -2
  2. openhands_sdk-1.53.0/openhands/sdk/automation/__init__.py +215 -0
  3. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/base.py +16 -1
  4. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/conversation.py +5 -0
  5. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/impl/local_conversation.py +2 -0
  6. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/impl/remote_conversation.py +36 -1
  7. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/request.py +8 -0
  8. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/observability/laminar.py +124 -16
  9. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/skills/skill.py +26 -3
  10. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/workspace/base.py +2 -1
  11. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/workspace/remote/async_remote_workspace.py +8 -0
  12. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/workspace/remote/base.py +8 -0
  13. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +17 -1
  14. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands_sdk.egg-info/PKG-INFO +2 -2
  15. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands_sdk.egg-info/SOURCES.txt +2 -0
  16. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands_sdk.egg-info/requires.txt +1 -1
  17. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/pyproject.toml +2 -2
  18. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/__init__.py +0 -0
  19. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/__init__.py +0 -0
  20. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/acp_agent.py +0 -0
  21. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/acp_file_credentials.py +0 -0
  22. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/acp_models.py +0 -0
  23. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/acp_tracing.py +0 -0
  24. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/agent.py +0 -0
  25. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/base.py +0 -0
  26. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/critic_mixin.py +0 -0
  27. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/parallel_executor.py +0 -0
  28. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/response_dispatch.py +0 -0
  29. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/stream_context.py +0 -0
  30. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/agent/utils.py +0 -0
  31. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/banner.py +0 -0
  32. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/__init__.py +0 -0
  33. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/agent_context.py +0 -0
  34. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/condenser/__init__.py +0 -0
  35. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/condenser/base.py +0 -0
  36. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +0 -0
  37. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
  38. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
  39. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/condenser/prompts/summarizing_events.j2 +0 -0
  40. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/condenser/prompts/summarizing_system.j2 +0 -0
  41. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/condenser/utils.py +0 -0
  42. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/memory.py +0 -0
  43. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/prompts/__init__.py +0 -0
  44. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/prompts/presets.py +0 -0
  45. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/prompts/prompt.py +0 -0
  46. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/prompts/registry.py +0 -0
  47. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/prompts/section.py +0 -0
  48. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
  49. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
  50. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/prompts/sections/planning.py +0 -0
  51. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/prompts/sections/static.py +0 -0
  52. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
  53. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
  54. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/view/__init__.py +0 -0
  55. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/view/manipulation_indices.py +0 -0
  56. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/view/properties/__init__.py +0 -0
  57. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/view/properties/base.py +0 -0
  58. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
  59. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
  60. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
  61. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
  62. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/context/view/view.py +0 -0
  63. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/__init__.py +0 -0
  64. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/cancellation.py +0 -0
  65. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/conversation_stats.py +0 -0
  66. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/event_store.py +0 -0
  67. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/events_list_base.py +0 -0
  68. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/exceptions.py +0 -0
  69. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/fifo_lock.py +0 -0
  70. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/goal/__init__.py +0 -0
  71. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/goal/controller.py +0 -0
  72. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/goal/judge.py +0 -0
  73. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/goal/prompts.py +0 -0
  74. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/goal/runner.py +0 -0
  75. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/impl/__init__.py +0 -0
  76. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/persistence_const.py +0 -0
  77. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
  78. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/response_utils.py +0 -0
  79. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/secret_registry.py +0 -0
  80. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/state.py +0 -0
  81. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/stuck_detector.py +0 -0
  82. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/title_utils.py +0 -0
  83. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/types.py +0 -0
  84. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
  85. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/visualizer/base.py +0 -0
  86. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/conversation/visualizer/default.py +0 -0
  87. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/credential.py +0 -0
  88. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/__init__.py +0 -0
  89. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/base.py +0 -0
  90. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/impl/__init__.py +0 -0
  91. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/impl/agent_finished.py +0 -0
  92. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/impl/api/__init__.py +0 -0
  93. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
  94. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/impl/api/client.py +0 -0
  95. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/impl/api/critic.py +0 -0
  96. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
  97. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/impl/empty_patch.py +0 -0
  98. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/impl/pass_critic.py +0 -0
  99. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/critic/result.py +0 -0
  100. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/__init__.py +0 -0
  101. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/acp_tool_call.py +0 -0
  102. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/base.py +0 -0
  103. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/condenser.py +0 -0
  104. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/conversation_error.py +0 -0
  105. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/conversation_state.py +0 -0
  106. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/error_classification.py +0 -0
  107. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/hook_execution.py +0 -0
  108. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/llm_completion_log.py +0 -0
  109. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
  110. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/llm_convertible/action.py +0 -0
  111. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/llm_convertible/message.py +0 -0
  112. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/llm_convertible/observation.py +0 -0
  113. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
  114. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/llm_convertible/system.py +0 -0
  115. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/resume_transcript.py +0 -0
  116. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/streaming_delta.py +0 -0
  117. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/token.py +0 -0
  118. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/types.py +0 -0
  119. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/event/user_action.py +0 -0
  120. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/extensions/__init__.py +0 -0
  121. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/extensions/fetch.py +0 -0
  122. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/extensions/installation/__init__.py +0 -0
  123. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/extensions/installation/info.py +0 -0
  124. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/extensions/installation/interface.py +0 -0
  125. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/extensions/installation/manager.py +0 -0
  126. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/extensions/installation/metadata.py +0 -0
  127. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/extensions/installation/utils.py +0 -0
  128. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/git/cached_repo.py +0 -0
  129. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/git/exceptions.py +0 -0
  130. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/git/git_changes.py +0 -0
  131. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/git/git_commits.py +0 -0
  132. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/git/git_diff.py +0 -0
  133. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/git/models.py +0 -0
  134. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/git/utils.py +0 -0
  135. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/hooks/__init__.py +0 -0
  136. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/hooks/config.py +0 -0
  137. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/hooks/conversation_hooks.py +0 -0
  138. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/hooks/executor.py +0 -0
  139. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/hooks/manager.py +0 -0
  140. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/hooks/types.py +0 -0
  141. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/io/__init__.py +0 -0
  142. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/io/base.py +0 -0
  143. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/io/cache.py +0 -0
  144. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/io/local.py +0 -0
  145. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/io/memory.py +0 -0
  146. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/launch/__init__.py +0 -0
  147. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/launch/errors.py +0 -0
  148. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/launch/finalize.py +0 -0
  149. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/launch/preview.py +0 -0
  150. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/launch/resolve.py +0 -0
  151. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/__init__.py +0 -0
  152. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/auth/__init__.py +0 -0
  153. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/auth/credentials.py +0 -0
  154. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/auth/openai.py +0 -0
  155. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/call_context.py +0 -0
  156. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/cleanup_profile.py +0 -0
  157. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/exceptions/__init__.py +0 -0
  158. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/exceptions/classifier.py +0 -0
  159. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/exceptions/mapping.py +0 -0
  160. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/exceptions/types.py +0 -0
  161. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/fallback_strategy.py +0 -0
  162. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/llm.py +0 -0
  163. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/llm_profile_store.py +0 -0
  164. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/llm_registry.py +0 -0
  165. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/llm_response.py +0 -0
  166. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/message.py +0 -0
  167. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/meta_profile_store.py +0 -0
  168. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
  169. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
  170. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
  171. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/options/__init__.py +0 -0
  172. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/options/chat_options.py +0 -0
  173. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/options/common.py +0 -0
  174. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/options/responses_options.py +0 -0
  175. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/provider_connection_store.py +0 -0
  176. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/router/__init__.py +0 -0
  177. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/router/base.py +0 -0
  178. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
  179. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/router/impl/random.py +0 -0
  180. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/streaming.py +0 -0
  181. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/image_inline.py +0 -0
  182. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/image_resize.py +0 -0
  183. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
  184. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/metrics.py +0 -0
  185. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/model_features.py +0 -0
  186. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/model_info.py +0 -0
  187. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
  188. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
  189. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
  190. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
  191. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
  192. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
  193. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
  194. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/telemetry.py +0 -0
  195. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/unverified_models.py +0 -0
  196. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/verified_models.py +0 -0
  197. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
  198. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/logger/__init__.py +0 -0
  199. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/logger/logger.py +0 -0
  200. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/logger/rolling.py +0 -0
  201. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/marketplace/__init__.py +0 -0
  202. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/marketplace/registration.py +0 -0
  203. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/marketplace/registry.py +0 -0
  204. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/marketplace/types.py +0 -0
  205. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/mcp/__init__.py +0 -0
  206. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/mcp/client.py +0 -0
  207. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/mcp/config.py +0 -0
  208. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/mcp/definition.py +0 -0
  209. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/mcp/exceptions.py +0 -0
  210. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/mcp/oauth.py +0 -0
  211. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/mcp/tool.py +0 -0
  212. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/mcp/utils.py +0 -0
  213. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/observability/__init__.py +0 -0
  214. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/observability/utils.py +0 -0
  215. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/__init__.py +0 -0
  216. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/discovery.py +0 -0
  217. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/fetch.py +0 -0
  218. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/format/__init__.py +0 -0
  219. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/format/agent_plugins.py +0 -0
  220. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/format/agent_plugins_mcp.py +0 -0
  221. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/format/base.py +0 -0
  222. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/format/claude_code.py +0 -0
  223. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/format/schemas/mcp-1.0.0.schema.json +0 -0
  224. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
  225. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/installed.py +0 -0
  226. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/loader.py +0 -0
  227. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/plugin.py +0 -0
  228. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/source.py +0 -0
  229. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/plugin/types.py +0 -0
  230. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/profiles/__init__.py +0 -0
  231. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/profiles/agent_profile.py +0 -0
  232. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/profiles/agent_profile_store.py +0 -0
  233. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/profiles/profile_refs.py +0 -0
  234. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/profiles/resolver.py +0 -0
  235. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/profiles/seed.py +0 -0
  236. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/py.typed +0 -0
  237. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/secret/__init__.py +0 -0
  238. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/secret/secrets.py +0 -0
  239. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/__init__.py +0 -0
  240. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/_shell_ast.py +0 -0
  241. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/analyzer.py +0 -0
  242. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/confirmation_policy.py +0 -0
  243. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
  244. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
  245. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
  246. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -0
  247. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
  248. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/ensemble.py +0 -0
  249. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/grayswan/__init__.py +0 -0
  250. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/grayswan/analyzer.py +0 -0
  251. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/grayswan/utils.py +0 -0
  252. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/llm_analyzer.py +0 -0
  253. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/risk.py +0 -0
  254. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/shell_parser.py +0 -0
  255. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/toolshield_helpers.py +0 -0
  256. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
  257. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/settings/__init__.py +0 -0
  258. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/settings/acp_install_catalog.py +0 -0
  259. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/settings/acp_providers.py +0 -0
  260. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/settings/api_models.py +0 -0
  261. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/settings/metadata.py +0 -0
  262. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/settings/model.py +0 -0
  263. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/skills/__init__.py +0 -0
  264. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/skills/exceptions.py +0 -0
  265. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/skills/execute.py +0 -0
  266. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/skills/fetch.py +0 -0
  267. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/skills/installed.py +0 -0
  268. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/skills/trigger.py +0 -0
  269. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/skills/types.py +0 -0
  270. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/skills/utils.py +0 -0
  271. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/subagent/__init__.py +0 -0
  272. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/subagent/load.py +0 -0
  273. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/subagent/registry.py +0 -0
  274. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/subagent/schema.py +0 -0
  275. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/subagent/scope.py +0 -0
  276. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/testing/__init__.py +0 -0
  277. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/testing/test_llm.py +0 -0
  278. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/__init__.py +0 -0
  279. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/builtins/__init__.py +0 -0
  280. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/builtins/classify_and_switch_llm.py +0 -0
  281. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/builtins/finish.py +0 -0
  282. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
  283. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
  284. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/builtins/think.py +0 -0
  285. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
  286. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/client_tool.py +0 -0
  287. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/defaults.py +0 -0
  288. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/registry.py +0 -0
  289. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/schema.py +0 -0
  290. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/spec.py +0 -0
  291. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/tool/tool.py +0 -0
  292. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/__init__.py +0 -0
  293. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/async_executor.py +0 -0
  294. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/async_utils.py +0 -0
  295. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/cipher.py +0 -0
  296. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/command.py +0 -0
  297. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/datetime.py +0 -0
  298. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/deprecation.py +0 -0
  299. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/files.py +0 -0
  300. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/github.py +0 -0
  301. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/json.py +0 -0
  302. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/masking.py +0 -0
  303. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/models.py +0 -0
  304. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/paging.py +0 -0
  305. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/path.py +0 -0
  306. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/pydantic_secrets.py +0 -0
  307. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/redact.py +0 -0
  308. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/truncate.py +0 -0
  309. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/utils/visualize.py +0 -0
  310. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/workspace/__init__.py +0 -0
  311. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/workspace/local.py +0 -0
  312. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/workspace/models.py +0 -0
  313. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/workspace/remote/__init__.py +0 -0
  314. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/workspace/repo.py +0 -0
  315. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands/sdk/workspace/workspace.py +0 -0
  316. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands_sdk.egg-info/dependency_links.txt +0 -0
  317. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/openhands_sdk.egg-info/top_level.txt +0 -0
  318. {openhands_sdk-1.51.0 → openhands_sdk-1.53.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-sdk
3
- Version: 1.51.0
3
+ Version: 1.53.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
@@ -18,7 +18,7 @@ Requires-Dist: joserfc>=1.7.5
18
18
  Requires-Dist: litellm>=1.93.0
19
19
  Requires-Dist: pillow>=12.3.0
20
20
  Requires-Dist: pydantic>=2.13.5
21
- Requires-Dist: python-frontmatter>=1.1.0
21
+ Requires-Dist: python-frontmatter>=1.3.0
22
22
  Requires-Dist: python-json-logger>=3.3.0
23
23
  Requires-Dist: tenacity>=9.1.2
24
24
  Requires-Dist: websockets>=12
@@ -0,0 +1,215 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from collections.abc import Mapping, Sequence
5
+ from typing import NotRequired, TypedDict
6
+
7
+ from openhands.sdk.conversation.types import TraceMetadataValue
8
+ from openhands.sdk.logger import get_logger
9
+ from openhands.sdk.observability.laminar import (
10
+ default_observability_span_name_from_env,
11
+ observability_metadata_from_env,
12
+ observability_parent_span_context_from_env,
13
+ observability_tags_from_env,
14
+ )
15
+
16
+
17
+ logger = get_logger(__name__)
18
+
19
+ _AUTOMATION_ID_KEY = "automation.id"
20
+ _AUTOMATION_NAME_KEY = "automation.name"
21
+ _AUTOMATION_RUN_ID_KEY = "automation.run_id"
22
+ _AUTOMATION_TRIGGER_SOURCE_KEY = "automation.trigger_source"
23
+ _DEFAULT_AUTOMATION_SPAN_NAME = "automation.conversation"
24
+
25
+
26
+ class AutomationObservabilityContext(TypedDict):
27
+ observability_metadata: NotRequired[dict[str, TraceMetadataValue]]
28
+ observability_tags: NotRequired[list[str]]
29
+ observability_span_name: NotRequired[str]
30
+ observability_parent_span_context: NotRequired[str]
31
+
32
+
33
+ class AutomationConversationKwargs(AutomationObservabilityContext, total=False):
34
+ tags: dict[str, str]
35
+
36
+
37
+ def _merge_metadata(
38
+ metadata: Mapping[str, TraceMetadataValue] | None,
39
+ ) -> dict[str, TraceMetadataValue]:
40
+ merged = observability_metadata_from_env()
41
+ if metadata:
42
+ merged.update(dict(metadata))
43
+ return merged
44
+
45
+
46
+ def _merge_tags(tags: Sequence[str] | None) -> list[str]:
47
+ merged: list[str] = []
48
+ seen: set[str] = set()
49
+ for tag in [*observability_tags_from_env(), *(tags or [])]:
50
+ tag = tag.strip()
51
+ if tag and tag not in seen:
52
+ seen.add(tag)
53
+ merged.append(tag)
54
+ return merged
55
+
56
+
57
+ def _metadata_str(metadata: Mapping[str, TraceMetadataValue], key: str) -> str | None:
58
+ value = metadata.get(key)
59
+ if value is None or isinstance(value, bool | list):
60
+ return None
61
+ return str(value)
62
+
63
+
64
+ def automation_observability_context(
65
+ *,
66
+ metadata: Mapping[str, TraceMetadataValue] | None = None,
67
+ tags: Sequence[str] | None = None,
68
+ span_name: str | None = None,
69
+ parent_span_context: str | None = None,
70
+ ) -> AutomationObservabilityContext:
71
+ """Collect generic automation observability fields for SDK conversations.
72
+
73
+ The automation service owns automation-specific values and supplies them via
74
+ generic ``OPENHANDS_OBSERVABILITY_*`` environment variables. This helper
75
+ collects those generic values and merges caller-provided additions, so custom
76
+ automation scripts do not need to duplicate the boilerplate for every
77
+ ``Conversation(...)`` call.
78
+ """
79
+ context: AutomationObservabilityContext = {}
80
+
81
+ merged_metadata = _merge_metadata(metadata)
82
+ if merged_metadata:
83
+ context["observability_metadata"] = merged_metadata
84
+
85
+ merged_tags = _merge_tags(tags)
86
+ if merged_tags:
87
+ context["observability_tags"] = merged_tags
88
+
89
+ resolved_span_name = span_name or default_observability_span_name_from_env()
90
+ if resolved_span_name:
91
+ context["observability_span_name"] = resolved_span_name
92
+
93
+ resolved_parent_context = (
94
+ parent_span_context or observability_parent_span_context_from_env()
95
+ )
96
+ if resolved_parent_context:
97
+ context["observability_parent_span_context"] = resolved_parent_context
98
+
99
+ return context
100
+
101
+
102
+ def automation_conversation_tags(
103
+ metadata: Mapping[str, TraceMetadataValue] | None = None,
104
+ ) -> dict[str, str]:
105
+ """Build product conversation tags from automation observability metadata."""
106
+ merged_metadata = _merge_metadata(metadata)
107
+ tags: dict[str, str] = {}
108
+
109
+ automation_id = _metadata_str(merged_metadata, _AUTOMATION_ID_KEY)
110
+ automation_name = _metadata_str(merged_metadata, _AUTOMATION_NAME_KEY)
111
+ automation_run_id = _metadata_str(merged_metadata, _AUTOMATION_RUN_ID_KEY)
112
+ trigger_source = _metadata_str(merged_metadata, _AUTOMATION_TRIGGER_SOURCE_KEY)
113
+
114
+ if automation_id or automation_name or automation_run_id or trigger_source:
115
+ tags["automationtrigger"] = "automation"
116
+ if automation_id:
117
+ tags["automationid"] = automation_id
118
+ if automation_name:
119
+ tags["automationname"] = automation_name
120
+ if automation_run_id:
121
+ tags["automationrunid"] = automation_run_id
122
+
123
+ return tags
124
+
125
+
126
+ def automation_conversation_kwargs(
127
+ *,
128
+ metadata: Mapping[str, TraceMetadataValue] | None = None,
129
+ observability_tags: Sequence[str] | None = None,
130
+ conversation_tags: Mapping[str, str] | None = None,
131
+ span_name: str | None = None,
132
+ parent_span_context: str | None = None,
133
+ include_conversation_tags: bool = True,
134
+ ) -> AutomationConversationKwargs:
135
+ """Return kwargs to pass into ``Conversation(...)`` from an automation script.
136
+
137
+ Example:
138
+ ``Conversation(..., **automation_conversation_kwargs())``
139
+ """
140
+ kwargs: AutomationConversationKwargs = {}
141
+ resolved_span_name = (
142
+ span_name
143
+ or default_observability_span_name_from_env()
144
+ or _DEFAULT_AUTOMATION_SPAN_NAME
145
+ )
146
+ context = automation_observability_context(
147
+ metadata=metadata,
148
+ tags=observability_tags,
149
+ span_name=resolved_span_name,
150
+ parent_span_context=parent_span_context,
151
+ )
152
+ if context_metadata := context.get("observability_metadata"):
153
+ kwargs["observability_metadata"] = context_metadata
154
+ if context_tags := context.get("observability_tags"):
155
+ kwargs["observability_tags"] = context_tags
156
+ if context_span_name := context.get("observability_span_name"):
157
+ kwargs["observability_span_name"] = context_span_name
158
+ if context_parent := context.get("observability_parent_span_context"):
159
+ kwargs["observability_parent_span_context"] = context_parent
160
+
161
+ if include_conversation_tags:
162
+ merged_conversation_tags = automation_conversation_tags(metadata)
163
+ if conversation_tags:
164
+ merged_conversation_tags.update(dict(conversation_tags))
165
+ if merged_conversation_tags:
166
+ kwargs["tags"] = merged_conversation_tags
167
+
168
+ return kwargs
169
+
170
+
171
+ def automation_observability_headers(
172
+ *,
173
+ metadata: Mapping[str, TraceMetadataValue] | None = None,
174
+ tags: Sequence[str] | None = None,
175
+ span_name: str | None = None,
176
+ parent_span_context: str | None = None,
177
+ ) -> dict[str, str]:
178
+ """Return observability headers for direct agent-server API calls."""
179
+ context = automation_observability_context(
180
+ metadata=metadata,
181
+ tags=tags,
182
+ span_name=span_name,
183
+ parent_span_context=parent_span_context,
184
+ )
185
+ headers: dict[str, str] = {}
186
+ if context_metadata := context.get("observability_metadata"):
187
+ headers["X-OpenHands-Observability-Metadata"] = json.dumps(
188
+ context_metadata, separators=(",", ":")
189
+ )
190
+ if context_tags := context.get("observability_tags"):
191
+ headers["X-OpenHands-Observability-Tags"] = ",".join(context_tags)
192
+ if context_span_name := context.get("observability_span_name"):
193
+ headers["X-OpenHands-Observability-Span-Name"] = context_span_name
194
+ if context_parent := context.get("observability_parent_span_context"):
195
+ headers["X-OpenHands-Observability-Parent-Span-Context"] = context_parent
196
+
197
+ try:
198
+ from opentelemetry.propagate import inject
199
+
200
+ inject(headers)
201
+ except Exception:
202
+ logger.debug(
203
+ "Failed to inject OpenTelemetry propagation headers", exc_info=True
204
+ )
205
+ return headers
206
+
207
+
208
+ __all__ = [
209
+ "AutomationConversationKwargs",
210
+ "AutomationObservabilityContext",
211
+ "automation_conversation_kwargs",
212
+ "automation_conversation_tags",
213
+ "automation_observability_context",
214
+ "automation_observability_headers",
215
+ ]
@@ -18,7 +18,10 @@ from openhands.sdk.llm.llm import LLM
18
18
  from openhands.sdk.llm.message import Message
19
19
  from openhands.sdk.observability.laminar import (
20
20
  RootSpan,
21
+ default_observability_span_name_from_env,
21
22
  end_root_span,
23
+ merge_observability_metadata,
24
+ observability_parent_span_context_from_env,
22
25
  should_enable_observability,
23
26
  start_child_span,
24
27
  start_root_span,
@@ -147,6 +150,7 @@ class BaseConversation(ABC):
147
150
  metadata: dict[str, TraceMetadataValue] | None = None,
148
151
  tags: list[str] | None = None,
149
152
  conversation_tags: Mapping[str, str] | None = None,
153
+ parent_span_context: str | None = None,
150
154
  ) -> None:
151
155
  """Start a per-conversation observability root span.
152
156
 
@@ -163,13 +167,24 @@ class BaseConversation(ABC):
163
167
  if self._observability_root_span is not None:
164
168
  # Idempotent: never start two roots for one conversation.
165
169
  return
170
+ default_span_name = default_observability_span_name_from_env()
171
+ if default_span_name and span_name == "conversation":
172
+ span_name = default_span_name
173
+
174
+ metadata = merge_observability_metadata(metadata)
175
+ attributes: dict[str, TraceMetadataValue] = dict(metadata)
176
+ tag_attributes = _conversation_tag_attributes(conversation_tags)
177
+ if tag_attributes:
178
+ attributes.update(tag_attributes)
166
179
  self._observability_root_span = start_root_span(
167
180
  "conversation",
168
181
  session_id=session_id,
169
182
  user_id=user_id,
170
183
  metadata=metadata,
171
184
  tags=tags,
172
- attributes=_conversation_tag_attributes(conversation_tags),
185
+ attributes=attributes,
186
+ parent_span_context=parent_span_context
187
+ or observability_parent_span_context_from_env(),
173
188
  )
174
189
  if span_name != "conversation":
175
190
  start_child_span(self._observability_root_span, span_name, tags=tags)
@@ -88,6 +88,7 @@ class Conversation:
88
88
  observability_metadata: dict[str, TraceMetadataValue] | None = None,
89
89
  observability_tags: list[str] | None = None,
90
90
  observability_span_name: str = "conversation",
91
+ observability_parent_span_context: str | None = None,
91
92
  ) -> "LocalConversation": ...
92
93
 
93
94
  @overload
@@ -117,6 +118,7 @@ class Conversation:
117
118
  observability_metadata: dict[str, TraceMetadataValue] | None = None,
118
119
  observability_tags: list[str] | None = None,
119
120
  observability_span_name: str = "conversation",
121
+ observability_parent_span_context: str | None = None,
120
122
  ) -> "RemoteConversation": ...
121
123
 
122
124
  def __new__(
@@ -146,6 +148,7 @@ class Conversation:
146
148
  observability_metadata: dict[str, TraceMetadataValue] | None = None,
147
149
  observability_tags: list[str] | None = None,
148
150
  observability_span_name: str = "conversation",
151
+ observability_parent_span_context: str | None = None,
149
152
  ) -> BaseConversation:
150
153
  from openhands.sdk.conversation.impl.local_conversation import LocalConversation
151
154
  from openhands.sdk.conversation.impl.remote_conversation import (
@@ -209,6 +212,7 @@ class Conversation:
209
212
  observability_metadata=observability_metadata,
210
213
  observability_tags=observability_tags,
211
214
  observability_span_name=observability_span_name,
215
+ observability_parent_span_context=observability_parent_span_context,
212
216
  )
213
217
 
214
218
  return LocalConversation(
@@ -232,4 +236,5 @@ class Conversation:
232
236
  observability_metadata=observability_metadata,
233
237
  observability_tags=observability_tags,
234
238
  observability_span_name=observability_span_name,
239
+ observability_parent_span_context=observability_parent_span_context,
235
240
  )
@@ -236,6 +236,7 @@ class LocalConversation(BaseConversation):
236
236
  # positional argument.
237
237
  max_budget_per_run: float | None = None,
238
238
  observability_span_name: str = "conversation",
239
+ observability_parent_span_context: str | None = None,
239
240
  prompt_cache_key: str | None = None,
240
241
  file_store: FileStore | None = None,
241
242
  mcp_tool_provider: MCPToolProvider | None = None,
@@ -552,6 +553,7 @@ class LocalConversation(BaseConversation):
552
553
  metadata=observability_metadata,
553
554
  tags=observability_tags,
554
555
  conversation_tags=tags,
556
+ parent_span_context=observability_parent_span_context,
555
557
  )
556
558
  self.delete_on_close = delete_on_close
557
559
 
@@ -53,7 +53,13 @@ from openhands.sdk.event.types import EventID
53
53
  from openhands.sdk.hooks import HookConfig
54
54
  from openhands.sdk.llm import LLM, Message, TextContent
55
55
  from openhands.sdk.logger import DEBUG, get_logger
56
- from openhands.sdk.observability.laminar import OPERATION_METADATA_KEY, observe
56
+ from openhands.sdk.observability.laminar import (
57
+ OPERATION_METADATA_KEY,
58
+ default_observability_span_name_from_env,
59
+ merge_observability_metadata,
60
+ observability_parent_span_context_from_env,
61
+ observe,
62
+ )
57
63
  from openhands.sdk.security.analyzer import SecurityAnalyzerBase
58
64
  from openhands.sdk.security.confirmation_policy import (
59
65
  ConfirmationPolicyBase,
@@ -739,6 +745,7 @@ class RemoteConversation(BaseConversation):
739
745
  observability_metadata: dict[str, TraceMetadataValue] | None = None,
740
746
  observability_tags: list[str] | None = None,
741
747
  observability_span_name: str = "conversation",
748
+ observability_parent_span_context: str | None = None,
742
749
  **_: object,
743
750
  ) -> None:
744
751
  """Remote conversation proxy that talks to an agent server.
@@ -777,6 +784,15 @@ class RemoteConversation(BaseConversation):
777
784
  observability_span_name: Optional child span name for observability
778
785
  backends. The root span remains named "conversation".
779
786
  """
787
+ observability_metadata = merge_observability_metadata(observability_metadata)
788
+ observability_parent_span_context = (
789
+ observability_parent_span_context
790
+ or observability_parent_span_context_from_env()
791
+ )
792
+ default_span_name = default_observability_span_name_from_env()
793
+ if default_span_name and observability_span_name == "conversation":
794
+ observability_span_name = default_span_name
795
+
780
796
  # Client tool specs the server already has persisted for this
781
797
  # conversation (populated when re-attaching to an existing one). These
782
798
  # must be registered locally before the initial event sync so that
@@ -860,6 +876,7 @@ class RemoteConversation(BaseConversation):
860
876
  if observability_tags is not None
861
877
  else [],
862
878
  "observability_span_name": observability_span_name,
879
+ "observability_parent_span_context": observability_parent_span_context,
863
880
  "user_id": user_id,
864
881
  }
865
882
  if user_id:
@@ -921,6 +938,7 @@ class RemoteConversation(BaseConversation):
921
938
  metadata=observability_metadata,
922
939
  tags=observability_tags,
923
940
  conversation_tags=tags,
941
+ parent_span_context=observability_parent_span_context,
924
942
  )
925
943
  # All hooks (including SessionStart/SessionEnd) are executed server-side.
926
944
  # hook_config is sent in the creation payload.
@@ -944,6 +962,22 @@ class RemoteConversation(BaseConversation):
944
962
  conversation ID follows the server's idempotency contract; this method
945
963
  does not probe for an existing conversation.
946
964
  """
965
+ updates: dict[str, object] = {
966
+ "observability_metadata": merge_observability_metadata(
967
+ request.observability_metadata
968
+ ),
969
+ }
970
+ default_span_name = default_observability_span_name_from_env()
971
+ if default_span_name and request.observability_span_name == "conversation":
972
+ updates["observability_span_name"] = default_span_name
973
+ parent_span_context = (
974
+ request.observability_parent_span_context
975
+ or observability_parent_span_context_from_env()
976
+ )
977
+ if parent_span_context:
978
+ updates["observability_parent_span_context"] = parent_span_context
979
+ request = request.model_copy(update=updates)
980
+
947
981
  response = _send_request(
948
982
  workspace.client,
949
983
  "POST",
@@ -962,6 +996,7 @@ class RemoteConversation(BaseConversation):
962
996
  metadata=request.observability_metadata,
963
997
  tags=request.observability_tags,
964
998
  conversation_tags=request.tags,
999
+ parent_span_context=request.observability_parent_span_context,
965
1000
  )
966
1001
  return conversation
967
1002
 
@@ -258,6 +258,14 @@ class ConversationConfig(BaseModel):
258
258
  "span names for grouping or signal routing."
259
259
  ),
260
260
  )
261
+ observability_parent_span_context: str | None = Field(
262
+ default=None,
263
+ description=(
264
+ "Serialized parent span context used to attach this conversation to "
265
+ "an upstream automation or integration trace."
266
+ ),
267
+ )
268
+
261
269
  autotitle: bool = Field(
262
270
  default=True,
263
271
  description=(
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import contextlib
4
4
  import functools
5
5
  import inspect
6
+ import json
6
7
  import sys
7
8
  from collections.abc import Callable, Iterator, Mapping
8
9
  from typing import TYPE_CHECKING, Any, Final, Literal, cast
@@ -35,6 +36,109 @@ _OBSERVABILITY_ENV_KEYS: Final[tuple[str, ...]] = (
35
36
  OPERATION_METADATA_KEY: Final[str] = "openhands.operation"
36
37
  """Metadata key naming the side-utility operation a span subtree belongs to."""
37
38
 
39
+ _OBSERVABILITY_METADATA_ENV: Final[str] = "OPENHANDS_OBSERVABILITY_METADATA"
40
+ _OBSERVABILITY_TAGS_ENV: Final[str] = "OPENHANDS_OBSERVABILITY_TAGS"
41
+ _OBSERVABILITY_SPAN_NAME_ENV: Final[str] = "OPENHANDS_OBSERVABILITY_SPAN_NAME"
42
+ _OBSERVABILITY_PARENT_CONTEXT_ENV: Final[str] = (
43
+ "OPENHANDS_OBSERVABILITY_PARENT_SPAN_CONTEXT"
44
+ )
45
+ _LAMINAR_PARENT_CONTEXT_ENV: Final[str] = "LMNR_SPAN_CONTEXT"
46
+
47
+
48
+ def _is_trace_metadata_value(value: Any) -> bool:
49
+ if isinstance(value, str | bool | int | float):
50
+ return True
51
+ if isinstance(value, list):
52
+ return (
53
+ all(isinstance(item, str) for item in value)
54
+ or all(isinstance(item, bool) for item in value)
55
+ or all(
56
+ isinstance(item, int) and not isinstance(item, bool) for item in value
57
+ )
58
+ or all(isinstance(item, float) for item in value)
59
+ )
60
+ return False
61
+
62
+
63
+ def _clean_trace_metadata(value: Any) -> dict[str, TraceMetadataValue]:
64
+ if not isinstance(value, dict):
65
+ return {}
66
+ cleaned: dict[str, TraceMetadataValue] = {}
67
+ for key, item in value.items():
68
+ if isinstance(key, str) and key and _is_trace_metadata_value(item):
69
+ cleaned[key] = item
70
+ return cleaned
71
+
72
+
73
+ def observability_metadata_from_env() -> dict[str, TraceMetadataValue]:
74
+ """Return generic observability metadata supplied by the process env."""
75
+ raw_metadata = get_env(_OBSERVABILITY_METADATA_ENV)
76
+ if not raw_metadata:
77
+ return {}
78
+ try:
79
+ return _clean_trace_metadata(json.loads(raw_metadata))
80
+ except json.JSONDecodeError:
81
+ logger.debug("Ignoring invalid %s", _OBSERVABILITY_METADATA_ENV)
82
+ return {}
83
+
84
+
85
+ def merge_observability_metadata(
86
+ metadata: Mapping[str, TraceMetadataValue] | None,
87
+ ) -> dict[str, TraceMetadataValue]:
88
+ """Merge generic env defaults with caller-supplied metadata."""
89
+ merged = observability_metadata_from_env()
90
+ if metadata:
91
+ merged.update(metadata)
92
+ return merged
93
+
94
+
95
+ def observability_parent_span_context_from_env() -> str | None:
96
+ """Return serialized parent span context propagated by the host process."""
97
+ return get_env(_OBSERVABILITY_PARENT_CONTEXT_ENV) or get_env(
98
+ _LAMINAR_PARENT_CONTEXT_ENV
99
+ )
100
+
101
+
102
+ def default_observability_span_name_from_env() -> str | None:
103
+ """Return the default conversation child span name from generic env."""
104
+ return get_env(_OBSERVABILITY_SPAN_NAME_ENV)
105
+
106
+
107
+ def observability_tags_from_env() -> list[str]:
108
+ """Return comma-separated observability tags supplied by generic env."""
109
+ raw_tags = get_env(_OBSERVABILITY_TAGS_ENV)
110
+ if not raw_tags:
111
+ return []
112
+ return [tag.strip() for tag in raw_tags.split(",") if tag.strip()]
113
+
114
+
115
+ def observability_headers_from_env() -> dict[str, str]:
116
+ """Build generic observability headers for agent-server API calls."""
117
+ headers: dict[str, str] = {}
118
+ metadata = observability_metadata_from_env()
119
+ if metadata:
120
+ headers["X-OpenHands-Observability-Metadata"] = json.dumps(
121
+ metadata, separators=(",", ":")
122
+ )
123
+ tags = observability_tags_from_env()
124
+ if tags:
125
+ headers["X-OpenHands-Observability-Tags"] = ",".join(tags)
126
+ span_name = default_observability_span_name_from_env()
127
+ if span_name:
128
+ headers["X-OpenHands-Observability-Span-Name"] = span_name
129
+ parent_context = observability_parent_span_context_from_env()
130
+ if parent_context:
131
+ headers["X-OpenHands-Observability-Parent-Span-Context"] = parent_context
132
+ try:
133
+ from opentelemetry.propagate import inject
134
+
135
+ inject(headers)
136
+ except Exception:
137
+ logger.debug(
138
+ "Failed to inject OpenTelemetry propagation headers", exc_info=True
139
+ )
140
+ return headers
141
+
38
142
 
39
143
  def _get_int_env(key: str) -> int | None:
40
144
  """Read an environment variable as an optional int."""
@@ -48,18 +152,6 @@ def _get_int_env(key: str) -> int | None:
48
152
  return None
49
153
 
50
154
 
51
- def _get_bool_env(key: str) -> bool:
52
- """Read an environment variable as a boolean.
53
-
54
- Returns True if the value is 'true', '1', 'yes', 'on' (case-insensitive).
55
- Returns False otherwise.
56
- """
57
- val = get_env(key)
58
- if val is None:
59
- return False
60
- return val.lower() in ("true", "1", "yes", "on")
61
-
62
-
63
155
  def maybe_init_laminar():
64
156
  """Initialize Laminar if the environment variables are set.
65
157
 
@@ -101,7 +193,12 @@ def maybe_init_laminar():
101
193
  return
102
194
 
103
195
  base_url = get_env("LMNR_BASE_URL") or None
104
- force_http = _get_bool_env("LMNR_FORCE_HTTP")
196
+ force_http = (get_env("LMNR_FORCE_HTTP") or "").lower() in (
197
+ "true",
198
+ "1",
199
+ "yes",
200
+ "on",
201
+ )
105
202
  instruments_env = get_env("LMNR_INSTRUMENTS")
106
203
  instruments = None
107
204
  if instruments_env is not None:
@@ -300,15 +397,24 @@ class RootSpan:
300
397
  name: str,
301
398
  session_id: str | None = None,
302
399
  user_id: str | None = None,
303
- attributes: Mapping[str, str] | None = None,
400
+ attributes: Mapping[str, TraceMetadataValue] | None = None,
304
401
  metadata: dict[str, TraceMetadataValue] | None = None,
305
402
  tags: list[str] | None = None,
403
+ parent_span_context: str | None = None,
306
404
  ) -> None:
307
405
  from lmnr import Laminar
308
406
 
407
+ parent = None
408
+ if parent_span_context:
409
+ with contextlib.suppress(Exception):
410
+ parent = Laminar.deserialize_span_context(parent_span_context)
411
+
309
412
  # ``start_span`` returns a span without attaching it as the current
310
413
  # OTel context; we'll restore it on every entry point via ``use_span``.
311
- self.span = Laminar.start_span(name)
414
+ if parent is not None:
415
+ self.span = Laminar.start_span(name, parent_span_context=parent)
416
+ else:
417
+ self.span = Laminar.start_span(name)
312
418
  if attributes:
313
419
  with contextlib.suppress(Exception):
314
420
  for key, value in attributes.items():
@@ -350,9 +456,10 @@ def start_root_span(
350
456
  name: str,
351
457
  session_id: str | None = None,
352
458
  user_id: str | None = None,
353
- attributes: Mapping[str, str] | None = None,
459
+ attributes: Mapping[str, TraceMetadataValue] | None = None,
354
460
  metadata: dict[str, TraceMetadataValue] | None = None,
355
461
  tags: list[str] | None = None,
462
+ parent_span_context: str | None = None,
356
463
  ) -> RootSpan | None:
357
464
  """Create a long-lived root span for an owning object.
358
465
 
@@ -368,6 +475,7 @@ def start_root_span(
368
475
  attributes=attributes,
369
476
  metadata=metadata,
370
477
  tags=tags,
478
+ parent_span_context=parent_span_context,
371
479
  )
372
480
  except Exception:
373
481
  logger.debug("Failed to create observability root span", exc_info=True)