openhands-sdk 1.54.0__tar.gz → 1.55.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.
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/PKG-INFO +1 -1
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/base.py +4 -1
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/impl/local_conversation.py +25 -9
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/impl/remote_conversation.py +0 -2
- openhands_sdk-1.55.0/openhands/sdk/conversation/message_request.py +19 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/request.py +57 -39
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/extensions/fetch.py +32 -10
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/extensions/installation/manager.py +57 -4
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/git/cached_repo.py +20 -5
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/git/git_diff.py +8 -2
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/git/utils.py +17 -3
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/launch/finalize.py +25 -2
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/launch/preview.py +11 -3
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/launch/resolve.py +47 -9
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/auth/openai.py +10 -1
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/model_features.py +3 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/telemetry.py +32 -6
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/unverified_models.py +8 -5
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/verified_models.py +14 -9
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/mcp/client.py +4 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/format/agent_plugins.py +14 -5
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/profiles/agent_profile.py +16 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/profiles/resolver.py +88 -47
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/profiles/seed.py +1 -5
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/_shell_ast.py +61 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/defense_in_depth/pattern.py +18 -9
- openhands_sdk-1.55.0/openhands/sdk/security/defense_in_depth/policy_rails.py +928 -0
- openhands_sdk-1.55.0/openhands/sdk/security/defense_in_depth/shell_semantics.py +1506 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/settings/acp_providers.py +7 -4
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/settings/model.py +1 -1
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/skills/skill.py +27 -10
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/skills/utils.py +6 -1
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/subagent/registry.py +29 -19
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/subagent/schema.py +4 -1
- openhands_sdk-1.55.0/openhands/sdk/subagent/scope.py +129 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/tool.py +3 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/redact.py +13 -29
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands_sdk.egg-info/PKG-INFO +1 -1
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands_sdk.egg-info/SOURCES.txt +2 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/pyproject.toml +1 -1
- openhands_sdk-1.54.0/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -185
- openhands_sdk-1.54.0/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -515
- openhands_sdk-1.54.0/openhands/sdk/subagent/scope.py +0 -81
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/acp_agent.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/acp_contracts.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/acp_file_credentials.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/acp_model_discovery.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/acp_models.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/acp_tracing.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/agent.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/critic_mixin.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/parallel_executor.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/response_dispatch.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/stream_context.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/agent/utils.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/automation/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/banner.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/agent_context.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/condenser/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/condenser/base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/condenser/prompts/summarizing_events.j2 +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/condenser/prompts/summarizing_system.j2 +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/condenser/utils.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/memory.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/prompts/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/prompts/presets.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/prompts/prompt.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/prompts/registry.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/prompts/section.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/prompts/sections/planning.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/prompts/sections/static.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/view/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/view/manipulation_indices.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/view/properties/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/view/properties/base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/context/view/view.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/cancellation.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/conversation.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/conversation_stats.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/event_store.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/events_list_base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/exceptions.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/fifo_lock.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/goal/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/goal/controller.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/goal/judge.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/goal/prompts.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/goal/runner.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/impl/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/persistence_const.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/response_utils.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/secret_registry.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/state.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/stuck_detector.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/title_utils.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/types.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/visualizer/base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/visualizer/default.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/credential.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/impl/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/impl/agent_finished.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/impl/api/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/impl/api/client.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/impl/api/critic.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/impl/empty_patch.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/impl/pass_critic.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/critic/result.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/acp_tool_call.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/condenser.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/conversation_error.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/conversation_state.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/error_classification.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/hook_execution.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/llm_completion_log.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/llm_convertible/action.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/llm_convertible/message.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/llm_convertible/observation.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/llm_convertible/system.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/resume_transcript.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/security.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/streaming_delta.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/token.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/types.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/event/user_action.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/extensions/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/extensions/installation/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/extensions/installation/info.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/extensions/installation/interface.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/extensions/installation/metadata.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/extensions/installation/utils.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/git/exceptions.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/git/git_changes.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/git/git_commits.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/git/models.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/hooks/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/hooks/config.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/hooks/conversation_hooks.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/hooks/executor.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/hooks/manager.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/hooks/types.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/io/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/io/base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/io/cache.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/io/local.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/io/memory.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/launch/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/launch/errors.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/_message_normalization.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/_response_stream.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/_tokenizer.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/auth/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/auth/credentials.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/call_context.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/cleanup_profile.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/exceptions/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/exceptions/classifier.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/exceptions/mapping.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/exceptions/types.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/fallback_strategy.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/llm.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/llm_profile_store.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/llm_registry.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/llm_response.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/message.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/meta_profile_store.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/options/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/options/chat_options.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/options/common.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/options/responses_options.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/provider_connection_store.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/router/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/router/base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/router/impl/random.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/streaming.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/image_inline.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/image_resize.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/metrics.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/model_info.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/logger/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/logger/logger.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/logger/rolling.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/marketplace/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/marketplace/registration.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/marketplace/registry.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/marketplace/types.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/mcp/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/mcp/config.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/mcp/definition.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/mcp/exceptions.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/mcp/oauth.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/mcp/tool.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/mcp/utils.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/observability/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/observability/laminar.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/observability/utils.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/discovery.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/fetch.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/format/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/format/agent_plugins_mcp.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/format/base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/format/claude_code.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/format/schemas/mcp-1.0.0.schema.json +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/installed.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/loader.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/plugin.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/source.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/plugin/types.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/profiles/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/profiles/agent_profile_store.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/profiles/profile_refs.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/py.typed +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/secret/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/secret/secrets.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/analyzer.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/confirmation_policy.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/ensemble.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/grayswan/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/grayswan/analyzer.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/grayswan/utils.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/llm_analyzer.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/risk.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/shell_parser.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/toolshield_helpers.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/settings/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/settings/acp_install_catalog.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/settings/api_models.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/settings/metadata.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/skills/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/skills/exceptions.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/skills/execute.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/skills/fetch.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/skills/installed.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/skills/trigger.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/skills/types.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/subagent/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/subagent/load.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/testing/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/testing/test_llm.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/builtins/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/builtins/classify_and_switch_llm.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/builtins/finish.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/builtins/think.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/client_tool.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/defaults.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/registry.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/schema.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/tool/spec.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/async_executor.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/async_utils.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/cipher.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/command.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/datetime.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/deprecation.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/files.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/github.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/json.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/masking.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/models.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/paging.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/path.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/pydantic_secrets.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/truncate.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/utils/visualize.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/workspace/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/workspace/base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/workspace/local.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/workspace/models.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/workspace/remote/__init__.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/workspace/remote/base.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/workspace/repo.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/workspace/workspace.py +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands_sdk.egg-info/dependency_links.txt +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands_sdk.egg-info/requires.txt +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands_sdk.egg-info/top_level.txt +0 -0
- {openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.55.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
|
|
@@ -444,7 +444,10 @@ class BaseConversation(ABC):
|
|
|
444
444
|
if ``None``).
|
|
445
445
|
agent: Agent for the fork. Defaults to a deep-copy of the
|
|
446
446
|
source agent.
|
|
447
|
-
title:
|
|
447
|
+
title: Deprecated on ``LocalConversation`` since 1.54.0 — has no
|
|
448
|
+
effect there and emits a ``DeprecationWarning``. Still
|
|
449
|
+
honoured by ``RemoteConversation`` (passed to the agent
|
|
450
|
+
server which stores it on ``StoredConversation``).
|
|
448
451
|
tags: Optional tags for the forked conversation.
|
|
449
452
|
reset_metrics: If ``True`` (default), cost/token stats start
|
|
450
453
|
fresh on the fork.
|
{openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/impl/local_conversation.py
RENAMED
|
@@ -112,6 +112,7 @@ from openhands.sdk.tool.builtins import InvokeSkillTool
|
|
|
112
112
|
from openhands.sdk.tool.client_tool import ClientToolSpec
|
|
113
113
|
from openhands.sdk.tool.schema import Action, Observation
|
|
114
114
|
from openhands.sdk.utils.cipher import Cipher
|
|
115
|
+
from openhands.sdk.utils.deprecation import warn_deprecated
|
|
115
116
|
from openhands.sdk.workspace import LocalWorkspace
|
|
116
117
|
|
|
117
118
|
|
|
@@ -204,6 +205,7 @@ class LocalConversation(BaseConversation):
|
|
|
204
205
|
_plugins_loaded: bool
|
|
205
206
|
_pending_hook_config: HookConfig | None # Hook config to combine with plugin hooks
|
|
206
207
|
_mcp_tool_provider: MCPToolProvider
|
|
208
|
+
_mcp_config_from_parent: bool
|
|
207
209
|
_llm_call_context: LLMCallContext
|
|
208
210
|
|
|
209
211
|
def __init__(
|
|
@@ -243,6 +245,7 @@ class LocalConversation(BaseConversation):
|
|
|
243
245
|
profile_store_dir: str | Path | None = None,
|
|
244
246
|
stream_callbacks: list[StreamProgressCallbackType] | None = None,
|
|
245
247
|
_parent_llm_call_context: LLMCallContext | None = None,
|
|
248
|
+
_mcp_config_from_parent: bool = False,
|
|
246
249
|
**_: object,
|
|
247
250
|
):
|
|
248
251
|
"""Initialize the conversation.
|
|
@@ -305,6 +308,9 @@ class LocalConversation(BaseConversation):
|
|
|
305
308
|
for state and EventLog storage.
|
|
306
309
|
_parent_llm_call_context: Runtime LLM context inherited by an internal
|
|
307
310
|
child conversation. Conversation-local identity is always replaced.
|
|
311
|
+
_mcp_config_from_parent: The agent's ``mcp_config`` is the resolved
|
|
312
|
+
config of a delegating parent. It is used as-is: no plugin MCP
|
|
313
|
+
servers are merged into it and no ``${VAR}`` is expanded again.
|
|
308
314
|
profile_store_dir: Optional directory containing saved LLM profiles.
|
|
309
315
|
Defaults to ``~/.openhands/profiles``.
|
|
310
316
|
"""
|
|
@@ -325,6 +331,7 @@ class LocalConversation(BaseConversation):
|
|
|
325
331
|
self._pending_hook_config = hook_config # Will be combined with plugin hooks
|
|
326
332
|
self._agent_ready = False # Agent initialized lazily after plugins loaded
|
|
327
333
|
self._mcp_tool_provider = mcp_tool_provider or DefaultMCPToolProvider()
|
|
334
|
+
self._mcp_config_from_parent = _mcp_config_from_parent
|
|
328
335
|
|
|
329
336
|
# Create-or-resume: factory inspects BASE_STATE to decide
|
|
330
337
|
desired_id = conversation_id or uuid.uuid4()
|
|
@@ -815,7 +822,10 @@ class LocalConversation(BaseConversation):
|
|
|
815
822
|
if ``None``).
|
|
816
823
|
agent: Agent for the fork. Defaults to a deep-copy of the
|
|
817
824
|
source agent.
|
|
818
|
-
title:
|
|
825
|
+
title: Deprecated. ``LocalConversation`` has no title field;
|
|
826
|
+
``ConversationState`` does not store titles (they live on
|
|
827
|
+
``StoredConversation`` in the agent-server layer). Passing a
|
|
828
|
+
value is accepted for backwards compatibility but has no effect.
|
|
819
829
|
tags: Optional tags for the forked conversation.
|
|
820
830
|
reset_metrics: If ``True`` (default), cost/token stats start
|
|
821
831
|
fresh on the fork.
|
|
@@ -830,6 +840,17 @@ class LocalConversation(BaseConversation):
|
|
|
830
840
|
Raises:
|
|
831
841
|
ValueError: If ``from_event_id`` is not an event in this conversation.
|
|
832
842
|
"""
|
|
843
|
+
if title is not None:
|
|
844
|
+
warn_deprecated(
|
|
845
|
+
"LocalConversation.fork() title parameter",
|
|
846
|
+
deprecated_in="1.54.0",
|
|
847
|
+
removed_in="1.60.0",
|
|
848
|
+
details=(
|
|
849
|
+
"LocalConversation has no title field; titles are managed by "
|
|
850
|
+
"StoredConversation in the agent-server layer."
|
|
851
|
+
),
|
|
852
|
+
stacklevel=2,
|
|
853
|
+
)
|
|
833
854
|
fork_id = conversation_id or uuid.uuid4()
|
|
834
855
|
# Always deep-copy the agent (supplied or source) so the fork owns
|
|
835
856
|
# its own object graph. Required because __init__ binds
|
|
@@ -911,13 +932,6 @@ class LocalConversation(BaseConversation):
|
|
|
911
932
|
)
|
|
912
933
|
fork_conv._state.agent_state = copy.deepcopy(self._state.agent_state)
|
|
913
934
|
|
|
914
|
-
# Copy title via tags if provided
|
|
915
|
-
if title is not None:
|
|
916
|
-
fork_conv._state.tags = {
|
|
917
|
-
**fork_conv._state.tags,
|
|
918
|
-
"title": title,
|
|
919
|
-
}
|
|
920
|
-
|
|
921
935
|
# Reset or copy metrics
|
|
922
936
|
if not reset_metrics:
|
|
923
937
|
fork_conv._state.stats = self._state.stats.model_copy(deep=True)
|
|
@@ -1215,7 +1229,9 @@ class LocalConversation(BaseConversation):
|
|
|
1215
1229
|
# - This is the ONLY place where defaults are applied (plugin loading preserves
|
|
1216
1230
|
# placeholders with expand_defaults=False to avoid double-expansion)
|
|
1217
1231
|
# Agent Plugins servers are left literal (see expand_mcp_servers).
|
|
1218
|
-
if
|
|
1232
|
+
if self._mcp_config_from_parent:
|
|
1233
|
+
merged_mcp = dict(self.agent.mcp_config)
|
|
1234
|
+
elif merged_mcp:
|
|
1219
1235
|
# Pass the registry's lookup method as a callback - secrets are retrieved
|
|
1220
1236
|
# lazily, one at a time, only when actually referenced in the config
|
|
1221
1237
|
merged_mcp = expand_mcp_servers(
|
{openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/conversation/impl/remote_conversation.py
RENAMED
|
@@ -1787,8 +1787,6 @@ class RemoteConversation(BaseConversation):
|
|
|
1787
1787
|
self.agent.model_dump(context={"expose_secrets": True}),
|
|
1788
1788
|
)
|
|
1789
1789
|
|
|
1790
|
-
# Use server-returned tags (which include merged title) rather than
|
|
1791
|
-
# the input tags, so the client-side object stays consistent.
|
|
1792
1790
|
server_tags: dict[str, str] | None = fork_info.get("tags") or None
|
|
1793
1791
|
|
|
1794
1792
|
return RemoteConversation(
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from openhands.sdk.llm.message import ImageContent, Message, TextContent
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SendMessageRequest(BaseModel):
|
|
9
|
+
"""Payload to send a message to the agent."""
|
|
10
|
+
|
|
11
|
+
role: Literal["user", "system", "assistant", "tool"] = "user"
|
|
12
|
+
content: list[TextContent | ImageContent] = Field(default_factory=list)
|
|
13
|
+
run: bool = Field(
|
|
14
|
+
default=False,
|
|
15
|
+
description="Whether the agent loop should automatically run if not running",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
def create_message(self) -> Message:
|
|
19
|
+
return Message(role=self.role, content=self.content)
|
|
@@ -8,7 +8,7 @@ agent-server.
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
-
from typing import Annotated, Any,
|
|
11
|
+
from typing import Annotated, Any, cast
|
|
12
12
|
from uuid import UUID
|
|
13
13
|
|
|
14
14
|
from pydantic import (
|
|
@@ -24,6 +24,9 @@ from pydantic import (
|
|
|
24
24
|
from openhands.sdk.agent.acp_agent import ACPAgent as ACPAgent
|
|
25
25
|
from openhands.sdk.agent.agent import Agent as Agent
|
|
26
26
|
from openhands.sdk.agent.base import AgentBase
|
|
27
|
+
from openhands.sdk.conversation.message_request import (
|
|
28
|
+
SendMessageRequest as SendMessageRequest,
|
|
29
|
+
)
|
|
27
30
|
from openhands.sdk.conversation.types import (
|
|
28
31
|
ConversationObservabilityMetadata,
|
|
29
32
|
ConversationObservabilitySpanName,
|
|
@@ -31,7 +34,6 @@ from openhands.sdk.conversation.types import (
|
|
|
31
34
|
ConversationTags,
|
|
32
35
|
)
|
|
33
36
|
from openhands.sdk.hooks import HookConfig
|
|
34
|
-
from openhands.sdk.llm.message import ImageContent, Message, TextContent
|
|
35
37
|
from openhands.sdk.plugin import PluginSource
|
|
36
38
|
from openhands.sdk.secret import SecretSource
|
|
37
39
|
from openhands.sdk.security.analyzer import SecurityAnalyzerBase
|
|
@@ -39,6 +41,8 @@ from openhands.sdk.security.confirmation_policy import (
|
|
|
39
41
|
ConfirmationPolicyBase,
|
|
40
42
|
NeverConfirm,
|
|
41
43
|
)
|
|
44
|
+
from openhands.sdk.settings.model import validate_agent_settings
|
|
45
|
+
from openhands.sdk.skills import Skill
|
|
42
46
|
from openhands.sdk.subagent.schema import AgentDefinition
|
|
43
47
|
from openhands.sdk.tool.client_tool import ClientToolSpec
|
|
44
48
|
from openhands.sdk.utils.models import kind_of
|
|
@@ -61,20 +65,6 @@ ACPEnabledAgent = Annotated[
|
|
|
61
65
|
# ---------------------------------------------------------------------------
|
|
62
66
|
|
|
63
67
|
|
|
64
|
-
class SendMessageRequest(BaseModel):
|
|
65
|
-
"""Payload to send a message to the agent."""
|
|
66
|
-
|
|
67
|
-
role: Literal["user", "system", "assistant", "tool"] = "user"
|
|
68
|
-
content: list[TextContent | ImageContent] = Field(default_factory=list)
|
|
69
|
-
run: bool = Field(
|
|
70
|
-
default=False,
|
|
71
|
-
description="Whether the agent loop should automatically run if not running",
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
def create_message(self) -> Message:
|
|
75
|
-
return Message(role=self.role, content=self.content)
|
|
76
|
-
|
|
77
|
-
|
|
78
68
|
class AgentLaunchAdditions(BaseModel):
|
|
79
69
|
"""Add deployment context after agent resolution."""
|
|
80
70
|
|
|
@@ -88,6 +78,30 @@ class AgentLaunchAdditions(BaseModel):
|
|
|
88
78
|
"system-message suffix."
|
|
89
79
|
),
|
|
90
80
|
)
|
|
81
|
+
skills: list[Skill] | None = Field(
|
|
82
|
+
default=None,
|
|
83
|
+
exclude_if=lambda value: value is None,
|
|
84
|
+
description=(
|
|
85
|
+
"Deployment-supplied skills merged into the resolved agent's "
|
|
86
|
+
"context. For a client that ships its own skill catalog and so "
|
|
87
|
+
"cannot rely on the server's: an ``agent_profile_id`` launch sends "
|
|
88
|
+
"no ``agent_settings``, leaving it no other way to supply them. "
|
|
89
|
+
"Merged by name — a skill the resolved agent already carries wins, "
|
|
90
|
+
"and the profile's ``disabled_skills`` deny-list still applies, so "
|
|
91
|
+
"this cannot reintroduce something the profile turned off. None "
|
|
92
|
+
"(the default) adds nothing."
|
|
93
|
+
),
|
|
94
|
+
)
|
|
95
|
+
llm_profile_ref: str | None = Field(
|
|
96
|
+
default=None,
|
|
97
|
+
min_length=1,
|
|
98
|
+
description=(
|
|
99
|
+
"LLM profile to launch an OpenHands Agent Profile with instead of "
|
|
100
|
+
"its own `llm_profile_ref`. Recorded in `launched_agent_profile`; "
|
|
101
|
+
"the stored profile is not modified. Only valid with "
|
|
102
|
+
"`agent_profile_id`."
|
|
103
|
+
),
|
|
104
|
+
)
|
|
91
105
|
|
|
92
106
|
|
|
93
107
|
class ConversationConfig(BaseModel):
|
|
@@ -291,9 +305,10 @@ class StartConversationRequest(ConversationConfig):
|
|
|
291
305
|
"""Payload to create a new conversation.
|
|
292
306
|
|
|
293
307
|
Extends :class:`ConversationConfig` with the agent source: a stored Agent
|
|
294
|
-
Profile (``agent_profile_id``), resolved agent settings
|
|
295
|
-
or an ``agent`` built in code. The
|
|
296
|
-
|
|
308
|
+
Profile (``agent_profile_id``), resolved agent settings
|
|
309
|
+
(``agent_settings``), or an ``agent`` built in code. The
|
|
310
|
+
server turns the source into the conversation's agent at launch; this model
|
|
311
|
+
only checks that exactly one source is given.
|
|
297
312
|
|
|
298
313
|
Note: the agent lives here on the *request*, deliberately not on
|
|
299
314
|
``ConversationConfig``. The persisted record (``StoredConversation``) does
|
|
@@ -313,7 +328,7 @@ class StartConversationRequest(ConversationConfig):
|
|
|
313
328
|
default=None,
|
|
314
329
|
description=(
|
|
315
330
|
"Stored Agent Profile to launch. The server resolves it with its own "
|
|
316
|
-
"stores. Mutually exclusive with
|
|
331
|
+
"stores. Mutually exclusive with the other agent sources."
|
|
317
332
|
),
|
|
318
333
|
)
|
|
319
334
|
agent: AgentBase = Field(default=cast(AgentBase, None))
|
|
@@ -323,25 +338,26 @@ class StartConversationRequest(ConversationConfig):
|
|
|
323
338
|
def _normalize_agent_source(cls, data: Any) -> Any:
|
|
324
339
|
if not isinstance(data, dict):
|
|
325
340
|
return data
|
|
326
|
-
#
|
|
341
|
+
# An explicit null means "not this source"; ``agent`` has no nullable
|
|
342
|
+
# type of its own.
|
|
327
343
|
payload = {
|
|
328
344
|
name: value
|
|
329
345
|
for name, value in data.items()
|
|
330
|
-
if
|
|
346
|
+
if value is not None or name not in ("agent", "agent_settings")
|
|
331
347
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
348
|
+
other_sources = [
|
|
349
|
+
name
|
|
350
|
+
for name in ("agent", "agent_settings")
|
|
351
|
+
if payload.get(name) is not None
|
|
352
|
+
]
|
|
353
|
+
if payload.get("agent_profile_id") is not None and other_sources:
|
|
336
354
|
raise ValueError(
|
|
337
|
-
"`agent_profile_id` is mutually exclusive with"
|
|
338
|
-
" `
|
|
355
|
+
"`agent_profile_id` is mutually exclusive with "
|
|
356
|
+
+ ", ".join(f"`{name}`" for name in other_sources)
|
|
339
357
|
)
|
|
340
|
-
if
|
|
358
|
+
if payload.get("agent") is not None:
|
|
341
359
|
payload["agent_settings"] = None
|
|
342
|
-
if
|
|
343
|
-
from openhands.sdk.settings.model import validate_agent_settings
|
|
344
|
-
|
|
360
|
+
if payload.get("agent") is None and payload.get("agent_settings") is not None:
|
|
345
361
|
try:
|
|
346
362
|
validate_agent_settings(payload["agent_settings"])
|
|
347
363
|
except (TypeError, ValueError) as exc:
|
|
@@ -355,14 +371,16 @@ class StartConversationRequest(ConversationConfig):
|
|
|
355
371
|
|
|
356
372
|
@model_validator(mode="after")
|
|
357
373
|
def _require_agent(self) -> StartConversationRequest:
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
374
|
+
has_profile = self.agent_profile_id is not None
|
|
375
|
+
if not has_profile and self.agent is None and self.agent_settings is None:
|
|
376
|
+
raise ValueError(
|
|
377
|
+
"One of `agent`, `agent_settings`, or `agent_profile_id` must be"
|
|
378
|
+
" provided"
|
|
379
|
+
)
|
|
380
|
+
additions = self.agent_launch_additions
|
|
381
|
+
if additions is not None and additions.llm_profile_ref and not has_profile:
|
|
363
382
|
raise ValueError(
|
|
364
|
-
"
|
|
365
|
-
" `agent_profile_id` must be provided"
|
|
383
|
+
"`agent_launch_additions.llm_profile_ref` requires `agent_profile_id`"
|
|
366
384
|
)
|
|
367
385
|
return self
|
|
368
386
|
|
|
@@ -23,7 +23,7 @@ class SourceType(StrEnum):
|
|
|
23
23
|
|
|
24
24
|
LOCAL -- a filesystem path (absolute, home-relative, or dot-relative).
|
|
25
25
|
GIT -- any git-clonable URL (HTTPS, SSH, git://, etc.).
|
|
26
|
-
GITHUB -- the ``github:owner/repo`` shorthand, expanded to an HTTPS URL.
|
|
26
|
+
GITHUB -- the ``github:owner/repo[/path]`` shorthand, expanded to an HTTPS URL.
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
29
|
LOCAL = "local"
|
|
@@ -31,12 +31,30 @@ class SourceType(StrEnum):
|
|
|
31
31
|
GITHUB = "github"
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def _split_github_shorthand(source: str) -> tuple[str, str | None]:
|
|
35
|
+
"""Split ``github:owner/repo[/subpath]`` into ``("owner/repo", subpath)``.
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
ExtensionFetchError: If owner or repo is missing.
|
|
39
|
+
"""
|
|
40
|
+
owner, _, rest = source.strip()[len("github:") :].partition("/")
|
|
41
|
+
repo, _, subpath = rest.partition("/")
|
|
42
|
+
if not owner or not repo:
|
|
43
|
+
raise ExtensionFetchError(
|
|
44
|
+
f"Invalid GitHub shorthand format: {source}. "
|
|
45
|
+
f"Expected format: github:owner/repo[/path]"
|
|
46
|
+
)
|
|
47
|
+
return f"{owner}/{repo}", subpath.strip("/") or None
|
|
48
|
+
|
|
49
|
+
|
|
34
50
|
def parse_extension_source(source: str) -> tuple[SourceType, str]:
|
|
35
51
|
"""Parse extension source into (SourceType, url).
|
|
36
52
|
|
|
37
53
|
Args:
|
|
38
54
|
source: Extension source string. Can be:
|
|
39
55
|
- "github:owner/repo" - GitHub repository shorthand
|
|
56
|
+
- "github:owner/repo/path" - the same, naming a subdirectory
|
|
57
|
+
(fetch() uses the path as ``repo_path``)
|
|
40
58
|
- "https://github.com/owner/repo.git" - Full git URL
|
|
41
59
|
- "git@github.com:owner/repo.git" - SSH git URL
|
|
42
60
|
- "/local/path" - Local path
|
|
@@ -57,16 +75,10 @@ def parse_extension_source(source: str) -> tuple[SourceType, str]:
|
|
|
57
75
|
"""
|
|
58
76
|
source = source.strip()
|
|
59
77
|
|
|
60
|
-
# GitHub shorthand: github:owner/repo
|
|
78
|
+
# GitHub shorthand: github:owner/repo[/subpath]
|
|
61
79
|
if source.startswith("github:"):
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if "/" not in repo_path or repo_path.count("/") > 1:
|
|
65
|
-
raise ExtensionFetchError(
|
|
66
|
-
f"Invalid GitHub shorthand format: {source}. "
|
|
67
|
-
f"Expected format: github:owner/repo"
|
|
68
|
-
)
|
|
69
|
-
url = f"https://github.com/{repo_path}.git"
|
|
80
|
+
repo, _ = _split_github_shorthand(source)
|
|
81
|
+
url = f"https://github.com/{repo}.git"
|
|
70
82
|
return (SourceType.GITHUB, url)
|
|
71
83
|
|
|
72
84
|
# Git URLs: detect by protocol/scheme rather than enumerating providers
|
|
@@ -194,6 +206,16 @@ def fetch_with_resolution(
|
|
|
194
206
|
"""
|
|
195
207
|
source_type, url = parse_extension_source(source)
|
|
196
208
|
|
|
209
|
+
if source_type == SourceType.GITHUB:
|
|
210
|
+
_, subpath = _split_github_shorthand(source)
|
|
211
|
+
if subpath is not None:
|
|
212
|
+
if repo_path:
|
|
213
|
+
raise ExtensionFetchError(
|
|
214
|
+
f"GitHub shorthand {source} already names a subdirectory; "
|
|
215
|
+
"do not also pass repo_path"
|
|
216
|
+
)
|
|
217
|
+
repo_path = subpath
|
|
218
|
+
|
|
197
219
|
if source_type == SourceType.LOCAL:
|
|
198
220
|
base_path = _resolve_local_source(url)
|
|
199
221
|
return _apply_subpath(base_path, repo_path, f"local source '{source}'"), None
|
{openhands_sdk-1.54.0 → openhands_sdk-1.55.0}/openhands/sdk/extensions/installation/manager.py
RENAMED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import shutil
|
|
4
|
+
import tempfile
|
|
4
5
|
from dataclasses import dataclass
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
7
|
-
from openhands.sdk.extensions.fetch import
|
|
8
|
+
from openhands.sdk.extensions.fetch import (
|
|
9
|
+
SourceType,
|
|
10
|
+
fetch_with_resolution,
|
|
11
|
+
parse_extension_source,
|
|
12
|
+
)
|
|
8
13
|
from openhands.sdk.extensions.installation.info import InstallationInfo
|
|
9
14
|
from openhands.sdk.extensions.installation.interface import (
|
|
10
15
|
ExtensionProtocol,
|
|
@@ -15,6 +20,7 @@ from openhands.sdk.extensions.installation.metadata import (
|
|
|
15
20
|
MetadataSession,
|
|
16
21
|
)
|
|
17
22
|
from openhands.sdk.extensions.installation.utils import validate_extension_name
|
|
23
|
+
from openhands.sdk.git.utils import extract_repo_name
|
|
18
24
|
from openhands.sdk.logger import get_logger
|
|
19
25
|
from openhands.sdk.utils.path import get_user_persistence_dir
|
|
20
26
|
from openhands.sdk.utils.redact import redact_url_credentials
|
|
@@ -82,6 +88,23 @@ class InstallationManager[T: ExtensionProtocol]:
|
|
|
82
88
|
FileExistsError: If extension is already installed and force=False.
|
|
83
89
|
ValueError: If the extension name is invalid.
|
|
84
90
|
"""
|
|
91
|
+
return self._install(source, ref=ref, repo_path=repo_path, force=force)
|
|
92
|
+
|
|
93
|
+
def _install(
|
|
94
|
+
self,
|
|
95
|
+
source: str | Path,
|
|
96
|
+
ref: str | None = None,
|
|
97
|
+
repo_path: str | None = None,
|
|
98
|
+
force: bool = False,
|
|
99
|
+
root_name: str | None = None,
|
|
100
|
+
) -> InstallationInfo:
|
|
101
|
+
"""Install as ``install`` does; ``root_name`` names a repository root.
|
|
102
|
+
|
|
103
|
+
``root_name`` replaces the repository's name as the directory name a
|
|
104
|
+
remote source without ``repo_path`` is loaded from (see
|
|
105
|
+
``_load_fetched``). ``update`` passes the installed name, so an
|
|
106
|
+
extension installed under another name keeps it.
|
|
107
|
+
"""
|
|
85
108
|
if isinstance(source, Path):
|
|
86
109
|
source = str(source)
|
|
87
110
|
|
|
@@ -94,7 +117,7 @@ class InstallationManager[T: ExtensionProtocol]:
|
|
|
94
117
|
update=True,
|
|
95
118
|
)
|
|
96
119
|
|
|
97
|
-
extension = self.
|
|
120
|
+
extension = self._load_fetched(fetched_path, source, repo_path, root_name)
|
|
98
121
|
validate_extension_name(extension.name)
|
|
99
122
|
|
|
100
123
|
install_path = self.installation_dir / extension.name
|
|
@@ -132,6 +155,34 @@ class InstallationManager[T: ExtensionProtocol]:
|
|
|
132
155
|
)
|
|
133
156
|
return info
|
|
134
157
|
|
|
158
|
+
def _load_fetched(
|
|
159
|
+
self,
|
|
160
|
+
fetched_path: Path,
|
|
161
|
+
source: str,
|
|
162
|
+
repo_path: str | None,
|
|
163
|
+
root_name: str | None = None,
|
|
164
|
+
) -> T:
|
|
165
|
+
"""Load the fetched extension, named after its repository if it is one.
|
|
166
|
+
|
|
167
|
+
A remote source without ``repo_path`` is fetched to the root of its
|
|
168
|
+
clone cache, ``<repo>-<hash>``. A loader that infers the name from the
|
|
169
|
+
directory (a plugin without a manifest) must see ``<repo>`` instead, as
|
|
170
|
+
it would for a local directory, so load from a copy with that name.
|
|
171
|
+
"""
|
|
172
|
+
source_type, url = parse_extension_source(source)
|
|
173
|
+
if repo_path or source_type == SourceType.LOCAL:
|
|
174
|
+
return self.installation_interface.load_from_dir(fetched_path)
|
|
175
|
+
|
|
176
|
+
with tempfile.TemporaryDirectory() as staging:
|
|
177
|
+
load_path = Path(staging) / (root_name or extract_repo_name(url))
|
|
178
|
+
shutil.copytree(
|
|
179
|
+
fetched_path,
|
|
180
|
+
load_path,
|
|
181
|
+
symlinks=True,
|
|
182
|
+
ignore=shutil.ignore_patterns(".git"),
|
|
183
|
+
)
|
|
184
|
+
return self.installation_interface.load_from_dir(load_path)
|
|
185
|
+
|
|
135
186
|
def uninstall(self, name: str) -> bool:
|
|
136
187
|
"""Uninstall an extension by name.
|
|
137
188
|
|
|
@@ -297,7 +348,8 @@ class InstallationManager[T: ExtensionProtocol]:
|
|
|
297
348
|
Re-fetches the extension from its original source with ``ref=None``
|
|
298
349
|
(i.e. the latest available) and force-reinstalls it. The previous
|
|
299
350
|
``enabled`` state is preserved because ``install(force=True)``
|
|
300
|
-
carries it over.
|
|
351
|
+
carries it over. A repository root without a manifest keeps its
|
|
352
|
+
installed name.
|
|
301
353
|
|
|
302
354
|
Args:
|
|
303
355
|
name: Name of the extension to update.
|
|
@@ -319,9 +371,10 @@ class InstallationManager[T: ExtensionProtocol]:
|
|
|
319
371
|
|
|
320
372
|
redacted = redact_url_credentials(current_info.source)
|
|
321
373
|
logger.info(f"Updating extension {name} from {redacted}")
|
|
322
|
-
return self.
|
|
374
|
+
return self._install(
|
|
323
375
|
source=current_info.source,
|
|
324
376
|
ref=None,
|
|
325
377
|
repo_path=current_info.repo_path,
|
|
326
378
|
force=True,
|
|
379
|
+
root_name=name,
|
|
327
380
|
)
|
|
@@ -201,6 +201,7 @@ def try_cached_clone_or_update(
|
|
|
201
201
|
update: bool = True,
|
|
202
202
|
git_helper: GitHelper | None = None,
|
|
203
203
|
lock_timeout: float = DEFAULT_LOCK_TIMEOUT,
|
|
204
|
+
strict: bool = False,
|
|
204
205
|
) -> Path | None:
|
|
205
206
|
"""Clone or update a git repository in a cache directory.
|
|
206
207
|
|
|
@@ -227,6 +228,9 @@ def try_cached_clone_or_update(
|
|
|
227
228
|
update: If True and repo exists, fetch and update it. If False, skip fetch.
|
|
228
229
|
git_helper: GitHelper instance for git operations. If None, creates one.
|
|
229
230
|
lock_timeout: Timeout in seconds for acquiring the lock. Default is 5 minutes.
|
|
231
|
+
strict: If True, an update whose fetch (or checkout of ``ref`` after the
|
|
232
|
+
fetch) fails returns None instead of the stale cached repository.
|
|
233
|
+
The cached repository is left on disk either way.
|
|
230
234
|
|
|
231
235
|
Returns:
|
|
232
236
|
Path to the local repository if successful, None on failure.
|
|
@@ -243,7 +247,7 @@ def try_cached_clone_or_update(
|
|
|
243
247
|
|
|
244
248
|
try:
|
|
245
249
|
with lock.acquire(timeout=lock_timeout):
|
|
246
|
-
return _do_clone_or_update(url, repo_path, ref, update, git)
|
|
250
|
+
return _do_clone_or_update(url, repo_path, ref, update, git, strict)
|
|
247
251
|
except Timeout:
|
|
248
252
|
logger.warning(
|
|
249
253
|
f"Timed out waiting for lock on {repo_path} after {lock_timeout}s"
|
|
@@ -263,6 +267,7 @@ def _do_clone_or_update(
|
|
|
263
267
|
ref: str | None,
|
|
264
268
|
update: bool,
|
|
265
269
|
git: GitHelper,
|
|
270
|
+
strict: bool = False,
|
|
266
271
|
) -> Path:
|
|
267
272
|
"""Perform the actual clone or update operation (called while holding lock).
|
|
268
273
|
|
|
@@ -272,6 +277,7 @@ def _do_clone_or_update(
|
|
|
272
277
|
ref: Branch, tag, or commit to checkout.
|
|
273
278
|
update: Whether to update existing repos.
|
|
274
279
|
git: GitHelper instance.
|
|
280
|
+
strict: Whether a failed update raises instead of keeping the cache.
|
|
275
281
|
|
|
276
282
|
Returns:
|
|
277
283
|
Path to the repository.
|
|
@@ -282,7 +288,7 @@ def _do_clone_or_update(
|
|
|
282
288
|
if repo_path.exists() and (repo_path / ".git").exists():
|
|
283
289
|
if update:
|
|
284
290
|
logger.debug(f"Updating repository at {repo_path}")
|
|
285
|
-
_update_repository(repo_path, ref, git)
|
|
291
|
+
_update_repository(repo_path, ref, git, strict=strict)
|
|
286
292
|
elif ref:
|
|
287
293
|
logger.debug(f"Checking out ref {ref} at {repo_path}")
|
|
288
294
|
_checkout_ref(repo_path, ref, git)
|
|
@@ -329,6 +335,7 @@ def _update_repository(
|
|
|
329
335
|
repo_path: Path,
|
|
330
336
|
ref: str | None,
|
|
331
337
|
git: GitHelper,
|
|
338
|
+
strict: bool = False,
|
|
332
339
|
) -> None:
|
|
333
340
|
"""Update an existing cached repository to the latest remote state.
|
|
334
341
|
|
|
@@ -354,6 +361,8 @@ def _update_repository(
|
|
|
354
361
|
ref: Branch, tag, or commit to update to. If None, uses current branch
|
|
355
362
|
or falls back to the remote's default branch.
|
|
356
363
|
git: GitHelper instance.
|
|
364
|
+
strict: If True, a failed fetch, or a failed checkout of ``ref`` after
|
|
365
|
+
the fetch, raises ``GitCommandError`` instead of keeping the cache.
|
|
357
366
|
"""
|
|
358
367
|
if ref:
|
|
359
368
|
# Optimistically attempt a local checkout before touching the network.
|
|
@@ -367,13 +376,19 @@ def _update_repository(
|
|
|
367
376
|
except GitCommandError:
|
|
368
377
|
pass # ref not cached locally; fall through to fetch
|
|
369
378
|
|
|
370
|
-
# Fetch from origin - if this fails, we still have a usable (stale) cache
|
|
371
|
-
|
|
379
|
+
# Fetch from origin - if this fails, we still have a usable (stale) cache;
|
|
380
|
+
# strict callers need to know the update did not happen.
|
|
381
|
+
if strict:
|
|
382
|
+
git.fetch(repo_path)
|
|
383
|
+
elif not _try_fetch(repo_path, git):
|
|
372
384
|
return
|
|
373
385
|
|
|
374
386
|
# If a specific ref was requested, check it out
|
|
375
387
|
if ref:
|
|
376
|
-
|
|
388
|
+
if strict:
|
|
389
|
+
_checkout_ref(repo_path, ref, git)
|
|
390
|
+
else:
|
|
391
|
+
_try_checkout_and_reset(repo_path, ref, git)
|
|
377
392
|
return
|
|
378
393
|
|
|
379
394
|
# No ref specified - update based on current state
|
|
@@ -108,8 +108,14 @@ def get_git_diff(relative_file_path: str | Path, ref: str | None = None) -> GitD
|
|
|
108
108
|
|
|
109
109
|
# Get old content (from the ref)
|
|
110
110
|
try:
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
# Read the blob verbatim and normalize it like the modified side below,
|
|
112
|
+
# so an unchanged file keeps its edge whitespace on both sides.
|
|
113
|
+
original = "\n".join(
|
|
114
|
+
run_git_command(
|
|
115
|
+
["git", "show", f"{current_rev}:{relative_path_from_repo}"],
|
|
116
|
+
validated_repo,
|
|
117
|
+
strip=False,
|
|
118
|
+
).splitlines()
|
|
113
119
|
)
|
|
114
120
|
except GitCommandError:
|
|
115
121
|
logger.debug(f"No old content found for {path} at ref {current_rev}")
|