openhands-sdk 1.55.1__tar.gz → 1.57.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 (330) hide show
  1. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/PKG-INFO +2 -2
  2. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/agent.py +7 -11
  3. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/base.py +5 -24
  4. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/base.py +4 -0
  5. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +4 -0
  6. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/base.py +29 -1
  7. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/impl/local_conversation.py +38 -10
  8. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/impl/remote_conversation.py +127 -44
  9. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/request.py +147 -2
  10. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/state.py +8 -8
  11. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/title_utils.py +29 -12
  12. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/action.py +30 -0
  13. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/resume_transcript.py +1 -1
  14. openhands_sdk-1.57.0/openhands/sdk/git/cached_repo.py +1181 -0
  15. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/git_changes.py +9 -3
  16. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/utils.py +2 -2
  17. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/llm_profile_store.py +7 -1
  18. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/meta_profile_store.py +4 -5
  19. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/unverified_models.py +16 -3
  20. openhands_sdk-1.57.0/openhands/sdk/logger/_rich_handler.py +62 -0
  21. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/logger/logger.py +3 -2
  22. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/marketplace/registry.py +44 -1
  23. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/marketplace/types.py +60 -0
  24. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/agent_profile.py +17 -8
  25. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/profile_refs.py +7 -2
  26. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/metadata.py +2 -0
  27. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/model.py +170 -68
  28. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/installed.py +6 -1
  29. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/skill.py +6 -8
  30. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/utils.py +10 -11
  31. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/defaults.py +0 -18
  32. openhands_sdk-1.57.0/openhands/sdk/utils/cipher.py +131 -0
  33. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/pydantic_secrets.py +49 -5
  34. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/redact.py +3 -0
  35. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands_sdk.egg-info/PKG-INFO +2 -2
  36. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands_sdk.egg-info/SOURCES.txt +2 -0
  37. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands_sdk.egg-info/requires.txt +1 -1
  38. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/pyproject.toml +2 -2
  39. openhands_sdk-1.55.1/openhands/sdk/git/cached_repo.py +0 -517
  40. openhands_sdk-1.55.1/openhands/sdk/utils/cipher.py +0 -81
  41. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/__init__.py +0 -0
  42. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/__init__.py +0 -0
  43. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_agent.py +0 -0
  44. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_contracts.py +0 -0
  45. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_file_credentials.py +0 -0
  46. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_model_discovery.py +0 -0
  47. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_models.py +0 -0
  48. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/acp_tracing.py +0 -0
  49. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/critic_mixin.py +0 -0
  50. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/parallel_executor.py +0 -0
  51. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/response_dispatch.py +0 -0
  52. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/stream_context.py +0 -0
  53. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/agent/utils.py +0 -0
  54. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/automation/__init__.py +0 -0
  55. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/banner.py +0 -0
  56. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/__init__.py +0 -0
  57. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/agent_context.py +0 -0
  58. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/__init__.py +0 -0
  59. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
  60. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
  61. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/prompts/summarizing_events.j2 +0 -0
  62. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/prompts/summarizing_system.j2 +0 -0
  63. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/condenser/utils.py +0 -0
  64. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/memory.py +0 -0
  65. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/__init__.py +0 -0
  66. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/presets.py +0 -0
  67. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/prompt.py +0 -0
  68. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/registry.py +0 -0
  69. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/section.py +0 -0
  70. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
  71. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
  72. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/sections/planning.py +0 -0
  73. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/sections/static.py +0 -0
  74. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
  75. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
  76. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/__init__.py +0 -0
  77. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/manipulation_indices.py +0 -0
  78. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/__init__.py +0 -0
  79. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/base.py +0 -0
  80. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
  81. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
  82. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
  83. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
  84. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/context/view/view.py +0 -0
  85. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/__init__.py +0 -0
  86. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/cancellation.py +0 -0
  87. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/conversation.py +0 -0
  88. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/conversation_stats.py +0 -0
  89. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/event_store.py +0 -0
  90. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/events_list_base.py +0 -0
  91. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/exceptions.py +0 -0
  92. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/fifo_lock.py +0 -0
  93. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/goal/__init__.py +0 -0
  94. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/goal/controller.py +0 -0
  95. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/goal/judge.py +0 -0
  96. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/goal/prompts.py +0 -0
  97. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/goal/runner.py +0 -0
  98. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/impl/__init__.py +0 -0
  99. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/message_request.py +0 -0
  100. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/persistence_const.py +0 -0
  101. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
  102. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/response_utils.py +0 -0
  103. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/secret_registry.py +0 -0
  104. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/stuck_detector.py +0 -0
  105. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/types.py +0 -0
  106. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
  107. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/visualizer/base.py +0 -0
  108. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/conversation/visualizer/default.py +0 -0
  109. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/credential.py +0 -0
  110. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/__init__.py +0 -0
  111. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/base.py +0 -0
  112. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/__init__.py +0 -0
  113. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/agent_finished.py +0 -0
  114. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/api/__init__.py +0 -0
  115. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
  116. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/api/client.py +0 -0
  117. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/api/critic.py +0 -0
  118. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
  119. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/empty_patch.py +0 -0
  120. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/impl/pass_critic.py +0 -0
  121. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/critic/result.py +0 -0
  122. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/__init__.py +0 -0
  123. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/acp_tool_call.py +0 -0
  124. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/base.py +0 -0
  125. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/condenser.py +0 -0
  126. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/conversation_error.py +0 -0
  127. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/conversation_state.py +0 -0
  128. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/error_classification.py +0 -0
  129. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/hook_execution.py +0 -0
  130. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_completion_log.py +0 -0
  131. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
  132. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/message.py +0 -0
  133. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/observation.py +0 -0
  134. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
  135. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/llm_convertible/system.py +0 -0
  136. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/security.py +0 -0
  137. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/streaming_delta.py +0 -0
  138. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/token.py +0 -0
  139. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/types.py +0 -0
  140. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/event/user_action.py +0 -0
  141. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/__init__.py +0 -0
  142. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/fetch.py +0 -0
  143. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/__init__.py +0 -0
  144. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/info.py +0 -0
  145. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/interface.py +0 -0
  146. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/manager.py +0 -0
  147. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/metadata.py +0 -0
  148. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/extensions/installation/utils.py +0 -0
  149. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/exceptions.py +0 -0
  150. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/git_commits.py +0 -0
  151. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/git_diff.py +0 -0
  152. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/git/models.py +0 -0
  153. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/__init__.py +0 -0
  154. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/config.py +0 -0
  155. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/conversation_hooks.py +0 -0
  156. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/executor.py +0 -0
  157. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/manager.py +0 -0
  158. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/hooks/types.py +0 -0
  159. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/io/__init__.py +0 -0
  160. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/io/base.py +0 -0
  161. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/io/cache.py +0 -0
  162. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/io/local.py +0 -0
  163. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/io/memory.py +0 -0
  164. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/launch/__init__.py +0 -0
  165. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/launch/errors.py +0 -0
  166. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/launch/finalize.py +0 -0
  167. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/launch/preview.py +0 -0
  168. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/launch/resolve.py +0 -0
  169. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/__init__.py +0 -0
  170. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/_message_normalization.py +0 -0
  171. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/_response_stream.py +0 -0
  172. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/_tokenizer.py +0 -0
  173. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/auth/__init__.py +0 -0
  174. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/auth/credentials.py +0 -0
  175. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/auth/openai.py +0 -0
  176. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/call_context.py +0 -0
  177. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/cleanup_profile.py +0 -0
  178. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/exceptions/__init__.py +0 -0
  179. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/exceptions/classifier.py +0 -0
  180. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/exceptions/mapping.py +0 -0
  181. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/exceptions/types.py +0 -0
  182. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/fallback_strategy.py +0 -0
  183. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/llm.py +0 -0
  184. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/llm_registry.py +0 -0
  185. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/llm_response.py +0 -0
  186. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/message.py +0 -0
  187. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
  188. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
  189. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
  190. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/options/__init__.py +0 -0
  191. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/options/chat_options.py +0 -0
  192. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/options/common.py +0 -0
  193. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/options/responses_options.py +0 -0
  194. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/provider_connection_store.py +0 -0
  195. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/router/__init__.py +0 -0
  196. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/router/base.py +0 -0
  197. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
  198. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/router/impl/random.py +0 -0
  199. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/streaming.py +0 -0
  200. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/image_inline.py +0 -0
  201. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/image_resize.py +0 -0
  202. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
  203. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/metrics.py +0 -0
  204. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/model_features.py +0 -0
  205. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/model_info.py +0 -0
  206. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
  207. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
  208. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
  209. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
  210. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
  211. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
  212. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
  213. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/telemetry.py +0 -0
  214. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/verified_models.py +0 -0
  215. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
  216. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/logger/__init__.py +0 -0
  217. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/logger/rolling.py +0 -0
  218. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/marketplace/__init__.py +0 -0
  219. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/marketplace/registration.py +0 -0
  220. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/__init__.py +0 -0
  221. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/client.py +0 -0
  222. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/config.py +0 -0
  223. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/definition.py +0 -0
  224. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/exceptions.py +0 -0
  225. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/oauth.py +0 -0
  226. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/tool.py +0 -0
  227. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/mcp/utils.py +0 -0
  228. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/observability/__init__.py +0 -0
  229. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/observability/laminar.py +0 -0
  230. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/observability/utils.py +0 -0
  231. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/__init__.py +0 -0
  232. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/discovery.py +0 -0
  233. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/fetch.py +0 -0
  234. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/__init__.py +0 -0
  235. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/agent_plugins.py +0 -0
  236. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/agent_plugins_mcp.py +0 -0
  237. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/base.py +0 -0
  238. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/claude_code.py +0 -0
  239. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/schemas/mcp-1.0.0.schema.json +0 -0
  240. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
  241. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/installed.py +0 -0
  242. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/loader.py +0 -0
  243. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/paths.py +0 -0
  244. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/plugin.py +0 -0
  245. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/source.py +0 -0
  246. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/strict_fetch.py +0 -0
  247. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/plugin/types.py +0 -0
  248. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/__init__.py +0 -0
  249. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/agent_profile_store.py +0 -0
  250. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/resolver.py +0 -0
  251. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/profiles/seed.py +0 -0
  252. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/py.typed +0 -0
  253. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/secret/__init__.py +0 -0
  254. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/secret/secrets.py +0 -0
  255. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/__init__.py +0 -0
  256. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/_shell_ast.py +0 -0
  257. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/analyzer.py +0 -0
  258. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/confirmation_policy.py +0 -0
  259. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
  260. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
  261. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
  262. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -0
  263. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
  264. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/ensemble.py +0 -0
  265. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/grayswan/__init__.py +0 -0
  266. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/grayswan/analyzer.py +0 -0
  267. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/grayswan/utils.py +0 -0
  268. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/llm_analyzer.py +0 -0
  269. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/risk.py +0 -0
  270. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/shell_parser.py +0 -0
  271. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/toolshield_helpers.py +0 -0
  272. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
  273. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/__init__.py +0 -0
  274. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/acp_install_catalog.py +0 -0
  275. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/acp_providers.py +0 -0
  276. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/settings/api_models.py +0 -0
  277. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/__init__.py +0 -0
  278. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/exceptions.py +0 -0
  279. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/execute.py +0 -0
  280. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/fetch.py +0 -0
  281. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/trigger.py +0 -0
  282. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/skills/types.py +0 -0
  283. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/subagent/__init__.py +0 -0
  284. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/subagent/load.py +0 -0
  285. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/subagent/registry.py +0 -0
  286. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/subagent/schema.py +0 -0
  287. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/subagent/scope.py +0 -0
  288. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/testing/__init__.py +0 -0
  289. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/testing/test_llm.py +0 -0
  290. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/__init__.py +0 -0
  291. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/__init__.py +0 -0
  292. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/classify_and_switch_llm.py +0 -0
  293. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/finish.py +0 -0
  294. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
  295. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
  296. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/think.py +0 -0
  297. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
  298. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/client_tool.py +0 -0
  299. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/registry.py +0 -0
  300. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/schema.py +0 -0
  301. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/spec.py +0 -0
  302. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/tool/tool.py +0 -0
  303. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/__init__.py +0 -0
  304. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/async_executor.py +0 -0
  305. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/async_utils.py +0 -0
  306. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/command.py +0 -0
  307. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/datetime.py +0 -0
  308. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/deprecation.py +0 -0
  309. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/files.py +0 -0
  310. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/github.py +0 -0
  311. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/json.py +0 -0
  312. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/masking.py +0 -0
  313. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/models.py +0 -0
  314. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/paging.py +0 -0
  315. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/path.py +0 -0
  316. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/truncate.py +0 -0
  317. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/utils/visualize.py +0 -0
  318. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/__init__.py +0 -0
  319. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/base.py +0 -0
  320. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/local.py +0 -0
  321. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/models.py +0 -0
  322. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/remote/__init__.py +0 -0
  323. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
  324. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/remote/base.py +0 -0
  325. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
  326. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/repo.py +0 -0
  327. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands/sdk/workspace/workspace.py +0 -0
  328. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands_sdk.egg-info/dependency_links.txt +0 -0
  329. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/openhands_sdk.egg-info/top_level.txt +0 -0
  330. {openhands_sdk-1.55.1 → openhands_sdk-1.57.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-sdk
3
- Version: 1.55.1
3
+ Version: 1.57.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
@@ -15,7 +15,7 @@ Requires-Dist: filelock>=3.20.1
15
15
  Requires-Dist: httpx[socks]>=0.28.1
16
16
  Requires-Dist: jsonschema>=4.23.0
17
17
  Requires-Dist: joserfc>=1.7.5
18
- Requires-Dist: litellm>=1.93.0
18
+ Requires-Dist: litellm<1.106,>=1.93.0
19
19
  Requires-Dist: pillow>=12.3.0
20
20
  Requires-Dist: pydantic>=2.13.5
21
21
  Requires-Dist: python-frontmatter>=1.3.0
@@ -625,6 +625,10 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
625
625
  preset = self._prompt_preset or PromptPreset.DEFAULT
626
626
  return create_registry(preset).build(ctx).dynamic
627
627
 
628
+ @staticmethod
629
+ def _mark_conversation_finished(state: ConversationState) -> None:
630
+ state.execution_status = ConversationExecutionStatus.FINISHED
631
+
628
632
  def _execute_actions(
629
633
  self,
630
634
  conversation: LocalConversation,
@@ -648,11 +652,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
648
652
  check_iterative_refinement=lambda ae: (
649
653
  self._check_iterative_refinement(conversation, ae)
650
654
  ),
651
- mark_finished=lambda: setattr(
652
- state,
653
- "execution_status",
654
- ConversationExecutionStatus.FINISHED,
655
- ),
655
+ mark_finished=lambda: self._mark_conversation_finished(state),
656
656
  )
657
657
 
658
658
  async def _aexecute_actions(
@@ -683,11 +683,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
683
683
  check_iterative_refinement=lambda ae: (
684
684
  self._check_iterative_refinement(conversation, ae)
685
685
  ),
686
- mark_finished=lambda: setattr(
687
- state,
688
- "execution_status",
689
- ConversationExecutionStatus.FINISHED,
690
- ),
686
+ mark_finished=lambda: self._mark_conversation_finished(state),
691
687
  )
692
688
 
693
689
  @observe(name="agent.step", ignore_inputs=["state", "on_event"])
@@ -1594,7 +1590,7 @@ class Agent(CriticMixin, ResponseDispatchMixin, AgentBase):
1594
1590
  condenser_config = self.condenser.model_dump(
1595
1591
  exclude={"llm"}, exclude_none=True
1596
1592
  )
1597
- condenser_llm_obj = getattr(self.condenser, "llm", None)
1593
+ condenser_llm_obj = self.condenser.get_llm()
1598
1594
  condenser_llm = (
1599
1595
  condenser_llm_obj.model if condenser_llm_obj is not None else "N/A"
1600
1596
  )
@@ -33,7 +33,6 @@ from openhands.sdk.mcp.client import MCPClient
33
33
  from openhands.sdk.mcp.config import MCPServer
34
34
  from openhands.sdk.mcp.tool import MCPToolDefinition, MCPToolExecutor
35
35
  from openhands.sdk.tool import (
36
- BROWSER_TOOL_NAME,
37
36
  BUILT_IN_TOOL_CLASSES,
38
37
  BUILT_IN_TOOLS,
39
38
  Tool,
@@ -45,7 +44,6 @@ from openhands.sdk.tool.builtins.vision_inspect import (
45
44
  VisionInspectTool,
46
45
  has_vision_profile_available,
47
46
  )
48
- from openhands.sdk.utils.deprecation import warn_deprecated
49
47
  from openhands.sdk.utils.models import DiscriminatedUnionMixin
50
48
  from openhands.sdk.utils.path import get_user_persistence_dir
51
49
 
@@ -268,21 +266,11 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
268
266
 
269
267
  @field_validator("system_prompt_kwargs")
270
268
  @classmethod
271
- def _warn_deprecated_enable_browser(
269
+ def _drop_retired_enable_browser(
272
270
  cls, value: dict[str, object]
273
271
  ) -> dict[str, object]:
274
- if "enable_browser" in value:
275
- warn_deprecated(
276
- "system_prompt_kwargs['enable_browser']",
277
- deprecated_in="1.51.0",
278
- removed_in="1.56.0",
279
- details=(
280
- "Browser guidance now comes from the loaded browser tools; "
281
- "only custom Jinja templates still read this kwarg."
282
- ),
283
- stacklevel=5,
284
- )
285
- return value
272
+ """Old saved conversations may still carry this retired prompt flag."""
273
+ return {key: item for key, item in value.items() if key != "enable_browser"}
286
274
 
287
275
  condenser: CondenserBase | None = Field(
288
276
  default=None,
@@ -378,15 +366,10 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
378
366
  # own Jinja template; everything else (incl. custom policies) uses the registry.
379
367
  preset = self._prompt_preset
380
368
  if preset is None:
381
- # Deprecated kwarg, kept until 1.56.0 for templates that branch on it.
382
- template_kwargs = {
383
- "enable_browser": any(t.name == BROWSER_TOOL_NAME for t in self.tools),
384
- **self._resolved_template_kwargs(),
385
- }
386
369
  return render_template(
387
370
  prompt_dir=self.prompt_dir,
388
371
  template_name=self.system_prompt_filename,
389
- **template_kwargs,
372
+ **self._resolved_template_kwargs(),
390
373
  )
391
374
 
392
375
  return create_registry(preset).build(self._build_prompt_context()).static
@@ -421,9 +404,7 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
421
404
  "model_family" not in template_kwargs
422
405
  or "model_variant" not in template_kwargs
423
406
  ):
424
- spec = get_model_prompt_spec(
425
- self.llm.model, getattr(self.llm, "model_canonical_name", None)
426
- )
407
+ spec = get_model_prompt_spec(self.llm.model, self.llm.model_canonical_name)
427
408
  if "model_family" not in template_kwargs and spec.family:
428
409
  template_kwargs["model_family"] = spec.family
429
410
  if "model_variant" not in template_kwargs and spec.variant:
@@ -29,6 +29,10 @@ class CondenserBase(DiscriminatedUnionMixin, ABC):
29
29
  step the condenser will use that condensation event to produce a new `View`.
30
30
  """
31
31
 
32
+ def get_llm(self) -> LLM | None:
33
+ """The LLM used by the condenser, if any."""
34
+ return None
35
+
32
36
  @abstractmethod
33
37
  def condense(self, view: View, agent_llm: LLM | None = None) -> View | Condensation:
34
38
  """Condense a sequence of events into a potentially smaller list.
@@ -112,6 +112,10 @@ class LLMSummarizingCondenser(RollingCondenser):
112
112
  def handles_condensation_requests(self) -> bool:
113
113
  return True
114
114
 
115
+ def get_llm(self) -> LLM | None:
116
+ """The LLM used by the condenser, if any."""
117
+ return self.llm
118
+
115
119
  def _effective_max_tokens(self, agent_llm: LLM | None) -> int | None:
116
120
  """Return the effective token cap that triggers token-based condensation.
117
121
 
@@ -2,7 +2,7 @@ import contextlib
2
2
  from abc import ABC, abstractmethod
3
3
  from collections.abc import AsyncIterator, Iterable, Mapping
4
4
  from pathlib import Path
5
- from typing import TYPE_CHECKING, Protocol, TypeVar, cast
5
+ from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast
6
6
 
7
7
  from openhands.sdk.conversation.conversation_stats import ConversationStats
8
8
  from openhands.sdk.conversation.events_list_base import EventsListBase
@@ -45,6 +45,7 @@ def _conversation_tag_attributes(
45
45
 
46
46
  if TYPE_CHECKING:
47
47
  from openhands.sdk.agent.base import AgentBase
48
+ from openhands.sdk.conversation.secret_registry import SecretRegistry
48
49
  from openhands.sdk.conversation.state import ConversationExecutionStatus
49
50
  from openhands.sdk.hooks import HookConfig
50
51
 
@@ -122,6 +123,11 @@ class ConversationStateProtocol(Protocol):
122
123
  """The hook configuration for this conversation."""
123
124
  ...
124
125
 
126
+ @property
127
+ def secret_registry(self) -> "SecretRegistry":
128
+ """The secret registry for this conversation."""
129
+ ...
130
+
125
131
 
126
132
  class BaseConversation(ABC):
127
133
  """Abstract base class for conversation implementations.
@@ -131,9 +137,16 @@ class BaseConversation(ABC):
131
137
  exchange, execution control, and state management.
132
138
  """
133
139
 
140
+ _cleanup_initiated: bool = False
141
+ _cleanup_complete: bool = False
142
+ _hook_processor: Any = None
143
+
134
144
  def __init__(self) -> None:
135
145
  """Initialize the base conversation with span tracking."""
136
146
  self._span_ended = False
147
+ self._cleanup_initiated = False
148
+ self._cleanup_complete = False
149
+ self._hook_processor = None
137
150
  # Owned root span. The ``observe`` decorator looks up this attribute
138
151
  # (by name ``_observability_root_span``) on ``self`` at every entry
139
152
  # point and re-attaches it via ``Laminar.use_span`` so that nested
@@ -142,6 +155,21 @@ class BaseConversation(ABC):
142
155
  # that constructed the conversation.
143
156
  self._observability_root_span: RootSpan | None = None
144
157
 
158
+ @property
159
+ def cleanup_initiated(self) -> bool:
160
+ """Whether conversation cleanup has started."""
161
+ return self._cleanup_initiated
162
+
163
+ @property
164
+ def cleanup_complete(self) -> bool:
165
+ """Whether conversation cleanup has finished."""
166
+ return self._cleanup_complete
167
+
168
+ @property
169
+ def hook_processor(self) -> Any:
170
+ """The active hook processor for the conversation, if configured."""
171
+ return self._hook_processor
172
+
145
173
  def _start_observability_span(
146
174
  self,
147
175
  session_id: str,
@@ -177,6 +177,16 @@ def _copy_event_for_fork(event: Event) -> Event:
177
177
  return Event.model_validate_json(event.model_dump_json(exclude_none=True))
178
178
 
179
179
 
180
+ def _same_llm_config(a: LLM, b: LLM) -> bool:
181
+ """Return whether two LLMs serialize to the same config, secrets included."""
182
+ if a is b:
183
+ return True
184
+ context = {"expose_secrets": True}
185
+ return a.model_dump(mode="json", context=context) == b.model_dump(
186
+ mode="json", context=context
187
+ )
188
+
189
+
180
190
  class LocalConversation(BaseConversation):
181
191
  agent: AgentBase
182
192
  workspace: LocalWorkspace
@@ -528,8 +538,8 @@ class LocalConversation(BaseConversation):
528
538
  # Idempotent with the create_request() lift; lower priority than
529
539
  # request.secrets (below). On resume, fill-if-absent so a persisted
530
540
  # value is never downgraded (or lost to redacted/no-cipher serialization).
531
- if (ctx := getattr(self.agent, "agent_context", None)) is not None:
532
- ctx_secrets = getattr(ctx, "secrets", None)
541
+ if self.agent is not None and self.agent.agent_context is not None:
542
+ ctx_secrets = self.agent.agent_context.secrets
533
543
  if ctx_secrets:
534
544
  existing_sources = self._state.secret_registry.secret_sources
535
545
  fill_secrets: dict[str, SecretValue] = {}
@@ -1664,18 +1674,31 @@ class LocalConversation(BaseConversation):
1664
1674
  """Swap the agent's LLM to the given object.
1665
1675
 
1666
1676
  The caller owns ``llm.usage_id``; it is the registry key. If an
1667
- entry with that key already exists, the cached LLM is reused and
1668
- the passed ``llm`` is dropped — matching the rest of the
1669
- registry's "first-write-wins" contract.
1677
+ entry with that key already exists and has the same config, the
1678
+ cached LLM is reused. If its config differs (e.g. ``default``, which
1679
+ agent initialization registers, posted with another model), the
1680
+ passed ``llm`` replaces the entry and takes over the metrics already
1681
+ recorded under that ``usage_id``.
1670
1682
 
1671
1683
  Args:
1672
1684
  llm: LLM to install on the agent.
1673
1685
  """
1686
+ usage_id = llm.usage_id
1674
1687
  try:
1675
- new_llm = self.llm_registry.get(llm.usage_id)
1688
+ registered = self.llm_registry.get(usage_id)
1676
1689
  except KeyError:
1690
+ registered = None
1691
+ if registered is not None and _same_llm_config(registered, llm):
1692
+ new_llm = registered
1693
+ else:
1677
1694
  new_llm = create_subscription_llm_from_config(llm)
1695
+ if registered is not None:
1696
+ self.llm_registry.remove(usage_id)
1678
1697
  self.llm_registry.add(new_llm)
1698
+ if registered is not None:
1699
+ # Keep one metrics object per usage_id: ConversationStats
1700
+ # tracks the registered LLM's, so accrued usage is retained.
1701
+ new_llm.restore_metrics(registered.metrics)
1679
1702
  # A switch_llm tool runs on a worker thread while run()/arun() holds the
1680
1703
  # state lock across the agent step on another thread, blocked awaiting
1681
1704
  # this very tool. Re-acquiring _state here would deadlock, so skip it:
@@ -1760,7 +1783,8 @@ class LocalConversation(BaseConversation):
1760
1783
 
1761
1784
  Raises:
1762
1785
  ValueError: If the conversation's agent is not an :class:`ACPAgent`,
1763
- or (for a live switch) the provider does not support runtime
1786
+ if ``model`` is empty or whitespace-only (live or not), or
1787
+ (for a live switch) the provider does not support runtime
1764
1788
  model switching, or the ACP server rejects the switch.
1765
1789
  TimeoutError: If a live switch's ``session/set_model`` round-trip
1766
1790
  exceeds ``acp_prompt_timeout`` seconds.
@@ -1769,6 +1793,10 @@ class LocalConversation(BaseConversation):
1769
1793
  raise ValueError(
1770
1794
  "switch_acp_model is only supported for ACP conversations."
1771
1795
  )
1796
+ # Check here, not only in set_acp_model: the pre-run path below skips
1797
+ # that call and would persist a blank model unchecked.
1798
+ if not model or not model.strip():
1799
+ raise ValueError("model must be a non-empty string")
1772
1800
  with self._state:
1773
1801
  # With a live session, perform the protocol switch first; if it
1774
1802
  # fails we leave the persisted state untouched. Without one (pre
@@ -2818,9 +2846,9 @@ class LocalConversation(BaseConversation):
2818
2846
 
2819
2847
  def close(self) -> None:
2820
2848
  """Close the conversation and clean up all tool executors."""
2821
- if getattr(self, "_cleanup_complete", False):
2849
+ if self._cleanup_complete:
2822
2850
  return
2823
- first_attempt = not getattr(self, "_cleanup_initiated", False)
2851
+ first_attempt = not self._cleanup_initiated
2824
2852
  if first_attempt:
2825
2853
  self._cleanup_initiated = True
2826
2854
 
@@ -2835,7 +2863,7 @@ class LocalConversation(BaseConversation):
2835
2863
  logger.debug(f"Could not register accumulated cost: {e}")
2836
2864
 
2837
2865
  logger.debug("Closing conversation and cleaning up tool executors")
2838
- hook_processor = getattr(self, "_hook_processor", None)
2866
+ hook_processor = self._hook_processor
2839
2867
  if hook_processor is not None:
2840
2868
  hook_processor.run_session_end()
2841
2869
  try:
@@ -13,6 +13,7 @@ from urllib.parse import urlparse
13
13
 
14
14
  import httpx
15
15
  import websockets
16
+ from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices
16
17
  from websockets.exceptions import ConnectionClosed
17
18
 
18
19
  from openhands.sdk.agent.base import AgentBase
@@ -28,12 +29,16 @@ from openhands.sdk.conversation.exceptions import (
28
29
  ConversationRunError,
29
30
  WebSocketConnectionError,
30
31
  )
31
- from openhands.sdk.conversation.secret_registry import SecretValue
32
+ from openhands.sdk.conversation.secret_registry import (
33
+ SecretRegistry,
34
+ SecretValue,
35
+ )
32
36
  from openhands.sdk.conversation.state import ConversationExecutionStatus
33
37
  from openhands.sdk.conversation.title_utils import generate_conversation_title
34
38
  from openhands.sdk.conversation.types import (
35
39
  ConversationCallbackType,
36
40
  ConversationID,
41
+ ConversationTokenCallbackType,
37
42
  StuckDetectionThresholds,
38
43
  TraceMetadataValue,
39
44
  )
@@ -49,6 +54,7 @@ from openhands.sdk.event.conversation_state import (
49
54
  ConversationStateUpdateEvent,
50
55
  )
51
56
  from openhands.sdk.event.llm_completion_log import LLMCompletionLogEvent
57
+ from openhands.sdk.event.streaming_delta import StreamingDeltaEvent
52
58
  from openhands.sdk.event.types import EventID
53
59
  from openhands.sdk.hooks import HookConfig
54
60
  from openhands.sdk.llm import LLM, Message, TextContent
@@ -92,6 +98,26 @@ def _validate_remote_agent(agent_data: dict) -> AgentBase:
92
98
  return AgentBase.model_validate(agent_data)
93
99
 
94
100
 
101
+ def _token_delta_callback(
102
+ token_callbacks: list[ConversationTokenCallbackType],
103
+ ) -> ConversationCallbackType:
104
+ """Hand each streamed delta from the events socket to the token callbacks."""
105
+ on_token = BaseConversation.compose_callbacks(token_callbacks)
106
+
107
+ def callback(event: Event) -> None:
108
+ if isinstance(event, StreamingDeltaEvent):
109
+ delta = Delta(
110
+ content=event.content, reasoning_content=event.reasoning_content
111
+ )
112
+ on_token(
113
+ ModelResponseStream(
114
+ id=event.id, choices=[StreamingChoices(index=0, delta=delta)]
115
+ )
116
+ )
117
+
118
+ return callback
119
+
120
+
95
121
  def _restore_tool_registrations(tool_module_qualnames: Mapping[str, str]) -> None:
96
122
  """Import the tool modules persisted with a remote conversation."""
97
123
  for tool_name, module_qualname in tool_module_qualnames.items():
@@ -493,9 +519,16 @@ class RemoteEventsList(EventsListBase):
493
519
  return len(self._cached_events) - 1
494
520
 
495
521
  def create_default_callback(self) -> ConversationCallbackType:
496
- """Create a default callback that adds events to this list."""
522
+ """Create a default callback that adds events to this list.
523
+
524
+ Streamed ``StreamingDeltaEvent`` frames are transient: the server never
525
+ stores them, so they reach the other callbacks but stay out of this
526
+ list, which keeps matching the server's event log.
527
+ """
497
528
 
498
529
  def callback(event: Event) -> None:
530
+ if isinstance(event, StreamingDeltaEvent):
531
+ return
499
532
  self.add_event(event)
500
533
 
501
534
  return callback
@@ -689,6 +722,15 @@ class RemoteState(ConversationStateProtocol):
689
722
  return HookConfig.model_validate(hook_config_data)
690
723
  return None
691
724
 
725
+ @property
726
+ def secret_registry(self) -> SecretRegistry:
727
+ """The secret registry for this conversation."""
728
+ info = self._get_conversation_info()
729
+ secret_registry_data = info.get("secret_registry")
730
+ if secret_registry_data is not None:
731
+ return SecretRegistry.model_validate(secret_registry_data)
732
+ return SecretRegistry()
733
+
692
734
  def model_dump(self, **_kwargs):
693
735
  """Get a dictionary representation of the remote state."""
694
736
  info = self._get_conversation_info()
@@ -746,6 +788,7 @@ class RemoteConversation(BaseConversation):
746
788
  observability_tags: list[str] | None = None,
747
789
  observability_span_name: str = "conversation",
748
790
  observability_parent_span_context: str | None = None,
791
+ token_callbacks: list[ConversationTokenCallbackType] | None = None,
749
792
  **_: object,
750
793
  ) -> None:
751
794
  """Remote conversation proxy that talks to an agent server.
@@ -783,7 +826,10 @@ class RemoteConversation(BaseConversation):
783
826
  observability_tags: Optional root span tags for observability backends.
784
827
  observability_span_name: Optional child span name for observability
785
828
  backends. The root span remains named "conversation".
829
+ token_callbacks: Optional callbacks invoked with each streamed token
830
+ delta the server sends (agent LLM with ``stream=True``).
786
831
  """
832
+ super().__init__()
787
833
  observability_metadata = merge_observability_metadata(observability_metadata)
788
834
  observability_parent_span_context = (
789
835
  observability_parent_span_context
@@ -920,6 +966,7 @@ class RemoteConversation(BaseConversation):
920
966
  workspace=workspace,
921
967
  conversation_id=conversation_id,
922
968
  callbacks=callbacks,
969
+ token_callbacks=token_callbacks,
923
970
  max_iteration_per_run=max_iteration_per_run,
924
971
  client_tools=[*(client_tools or []), *attached_client_tools],
925
972
  visualizer=visualizer,
@@ -1060,10 +1107,13 @@ class RemoteConversation(BaseConversation):
1060
1107
  visualizer: (
1061
1108
  type[ConversationVisualizerBase] | ConversationVisualizerBase | None
1062
1109
  ),
1110
+ token_callbacks: list[ConversationTokenCallbackType] | None = None,
1063
1111
  ) -> None:
1064
1112
  super().__init__() # Initialize base class with span tracking
1065
1113
  self.agent = agent
1066
1114
  self._callbacks = callbacks or []
1115
+ if token_callbacks:
1116
+ self._callbacks.append(_token_delta_callback(token_callbacks))
1067
1117
  self.max_iteration_per_run = max_iteration_per_run
1068
1118
  self.workspace = workspace
1069
1119
  self._client = workspace.client
@@ -1166,13 +1216,40 @@ class RemoteConversation(BaseConversation):
1166
1216
  all_callbacks = self._callbacks + [run_complete_callback]
1167
1217
  composed_callback = BaseConversation.compose_callbacks(all_callbacks)
1168
1218
 
1219
+ # Track which events the callbacks have seen, so that events the
1220
+ # server recorded while the socket was reconnecting (and that only
1221
+ # reached the cache through REST reconciliation) can be replayed to
1222
+ # them once, without the live socket delivering them a second time.
1223
+ delivered_ids: set[str] = set()
1224
+ replayed_ids: set[str] = set()
1225
+
1226
+ def socket_callback(event: Event) -> None:
1227
+ if event.id in replayed_ids:
1228
+ replayed_ids.discard(event.id)
1229
+ return
1230
+ delivered_ids.add(event.id)
1231
+ composed_callback(event)
1232
+
1233
+ def replay_missed_events() -> None:
1234
+ self._state.events.reconcile()
1235
+ replayed_ids.clear()
1236
+ for event in self._state.events[:]:
1237
+ if event.id in delivered_ids:
1238
+ continue
1239
+ delivered_ids.add(event.id)
1240
+ replayed_ids.add(event.id)
1241
+ try:
1242
+ composed_callback(event)
1243
+ except Exception:
1244
+ logger.exception("ws_replay_callback_error", stack_info=True)
1245
+
1169
1246
  # Initialize WebSocket client for callbacks
1170
1247
  self._ws_client = WebSocketCallbackClient(
1171
1248
  host=self.workspace.host,
1172
1249
  conversation_id=str(self._id),
1173
- callback=composed_callback,
1250
+ callback=socket_callback,
1174
1251
  api_key=self.workspace.api_key,
1175
- on_reconnect=self._state.events.reconcile,
1252
+ on_reconnect=replay_missed_events,
1176
1253
  )
1177
1254
  self._ws_client.start()
1178
1255
 
@@ -1210,6 +1287,9 @@ class RemoteConversation(BaseConversation):
1210
1287
  # were emitted between the initial REST sync and WebSocket subscription.
1211
1288
  # This is the "reconciliation" part of the subscription handshake.
1212
1289
  self._state.events.reconcile()
1290
+ # Callbacks are not replayed history: only events recorded after this
1291
+ # point and missed by the socket are replayed on reconnect.
1292
+ delivered_ids.update(event.id for event in self._state.events[:])
1213
1293
 
1214
1294
  def _create_llm_completion_log_callback(self) -> ConversationCallbackType:
1215
1295
  """Create a callback that writes LLM completion logs to client filesystem."""
@@ -1850,52 +1930,55 @@ class RemoteConversation(BaseConversation):
1850
1930
  The workspace owns the client and will close it during its own cleanup.
1851
1931
  Closing it here would prevent the workspace from making cleanup API calls.
1852
1932
  """
1853
- if self._cleanup_initiated:
1933
+ if self._cleanup_complete:
1854
1934
  return
1855
- self._cleanup_initiated = True
1856
-
1857
- # Best-effort: hand the accumulated LLM cost to the workspace so it can
1858
- # be included in the automation completion callback. Only read cached
1859
- # state — close() also runs on the failure path, where a live fetch
1860
- # could block until timeout against an agent server that is already gone.
1861
- # The cache tracks this run: the agent server streams a "stats" update
1862
- # after every LLM response (EventService._setup_stats_streaming), so it
1863
- # holds the run's spend even on the failure path, which wakes run() from
1864
- # a per-field ERROR/STUCK update before the post-run full-state snapshot.
1865
- try:
1866
- cached = self._state._cached_state
1867
- # Require an actual "stats" entry: a cache built only from partial
1868
- # field updates — e.g. the subscribe-time push for a service with no
1869
- # live conversation — would otherwise yield 0.0 and record a run as
1870
- # free when its cost is really just unknown.
1871
- if cached is not None and "stats" in cached:
1872
- cost = self._state.stats.get_combined_metrics().accumulated_cost
1873
- self.workspace.register_cost(cost)
1874
- except Exception as e:
1875
- logger.debug(f"Could not register accumulated cost: {e}")
1876
-
1877
- # SessionEnd hooks are executed server-side (via hook_config in payload).
1878
- try:
1879
- # Stop WebSocket client if it exists
1880
- if self._ws_client:
1881
- self._ws_client.stop()
1882
- self._ws_client = None
1883
- except Exception:
1884
- pass
1935
+ first_attempt = not self._cleanup_initiated
1936
+ if first_attempt:
1937
+ self._cleanup_initiated = True
1938
+
1939
+ # Best-effort: hand the accumulated LLM cost to the workspace so it can
1940
+ # be included in the automation completion callback. Only read cached
1941
+ # state — close() also runs on the failure path, where a live fetch
1942
+ # could block until timeout against an agent server that is already gone.
1943
+ # The cache tracks this run: the agent server streams a "stats" update
1944
+ # after every LLM response (EventService._setup_stats_streaming), so it
1945
+ # holds the run's spend even on the failure path, which wakes run() from
1946
+ # a per-field ERROR/STUCK update before the post-run full-state snapshot.
1947
+ try:
1948
+ cached = self._state._cached_state
1949
+ # Require an actual "stats" entry: a cache built only from partial
1950
+ # field updates — e.g. the subscribe-time push for a service with no
1951
+ # live conversation — would otherwise yield 0.0 and record a run as
1952
+ # free when its cost is really just unknown.
1953
+ if cached is not None and "stats" in cached:
1954
+ cost = self._state.stats.get_combined_metrics().accumulated_cost
1955
+ self.workspace.register_cost(cost)
1956
+ except Exception as e:
1957
+ logger.debug(f"Could not register accumulated cost: {e}")
1885
1958
 
1886
- self._end_observability_span()
1887
- if self.delete_on_close:
1959
+ # SessionEnd hooks are executed server-side (via hook_config in payload).
1888
1960
  try:
1889
- # trigger server-side delete_conversation to release resources
1890
- # like tmux sessions
1891
- _send_request(
1892
- self._client,
1893
- "DELETE",
1894
- f"{CONVERSATIONS_PATH}/{self.id}",
1895
- )
1961
+ # Stop WebSocket client if it exists
1962
+ if self._ws_client:
1963
+ self._ws_client.stop()
1964
+ self._ws_client = None
1896
1965
  except Exception:
1897
1966
  pass
1898
1967
 
1968
+ self._end_observability_span()
1969
+ if self.delete_on_close:
1970
+ try:
1971
+ # trigger server-side delete_conversation to release resources
1972
+ # like tmux sessions
1973
+ _send_request(
1974
+ self._client,
1975
+ "DELETE",
1976
+ f"{CONVERSATIONS_PATH}/{self.id}",
1977
+ )
1978
+ except Exception:
1979
+ pass
1980
+ self._cleanup_complete = True
1981
+
1899
1982
  def __del__(self) -> None:
1900
1983
  try:
1901
1984
  self.close()