openhands-sdk 1.49.1__tar.gz → 1.49.2__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.49.1 → openhands_sdk-1.49.2}/PKG-INFO +2 -2
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/impl/local_conversation.py +6 -23
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/config.py +18 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/utils.py +56 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/__init__.py +2 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/format/__init__.py +7 -12
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/format/agent_plugins.py +37 -17
- openhands_sdk-1.49.2/openhands/sdk/plugin/format/agent_plugins_mcp.py +371 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/format/base.py +56 -12
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/format/claude_code.py +9 -3
- openhands_sdk-1.49.2/openhands/sdk/plugin/format/schemas/mcp-1.0.0.schema.json +120 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/loader.py +2 -9
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/skill.py +7 -2
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/utils.py +57 -8
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/subagent/load.py +8 -2
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/path.py +14 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands_sdk.egg-info/PKG-INFO +2 -2
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands_sdk.egg-info/SOURCES.txt +4 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands_sdk.egg-info/requires.txt +1 -1
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/pyproject.toml +4 -2
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/acp_agent.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/acp_file_credentials.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/acp_models.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/acp_tracing.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/agent.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/critic_mixin.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/parallel_executor.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/response_dispatch.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/stream_context.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/utils.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/banner.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/agent_context.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/utils.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/memory.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/presets.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/prompt.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/registry.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/section.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/sections/planning.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/sections/static.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/manipulation_indices.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/view.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/cancellation.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/conversation.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/conversation_stats.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/event_store.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/events_list_base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/exceptions.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/fifo_lock.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/goal/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/goal/controller.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/goal/judge.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/goal/prompts.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/goal/runner.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/impl/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/impl/remote_conversation.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/persistence_const.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/request.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/response_utils.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/secret_registry.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/state.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/stuck_detector.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/title_utils.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/types.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/visualizer/base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/visualizer/default.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/credential.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/agent_finished.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/api/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/api/client.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/api/critic.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/empty_patch.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/pass_critic.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/result.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/acp_tool_call.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/condenser.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/conversation_error.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/conversation_state.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/error_classification.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/hook_execution.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_completion_log.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/action.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/message.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/observation.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/system.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/resume_transcript.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/streaming_delta.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/token.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/types.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/user_action.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/fetch.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/info.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/interface.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/manager.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/metadata.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/utils.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/cached_repo.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/exceptions.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/git_changes.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/git_commits.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/git_diff.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/models.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/utils.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/config.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/conversation_hooks.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/executor.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/manager.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/types.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/io/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/io/base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/io/cache.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/io/local.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/io/memory.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/auth/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/auth/credentials.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/auth/openai.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/cleanup_profile.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/exceptions/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/exceptions/classifier.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/exceptions/mapping.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/exceptions/types.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/fallback_strategy.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/llm.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/llm_profile_store.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/llm_registry.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/llm_response.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/message.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/options/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/options/chat_options.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/options/common.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/options/responses_options.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/provider_connection_store.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/router/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/router/base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/router/impl/random.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/streaming.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/image_inline.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/image_resize.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/metrics.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/model_features.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/model_info.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/telemetry.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/unverified_models.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/verified_models.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/logger/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/logger/logger.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/logger/rolling.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/marketplace/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/marketplace/registration.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/marketplace/registry.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/marketplace/types.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/client.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/definition.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/exceptions.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/tool.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/observability/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/observability/laminar.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/observability/utils.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/discovery.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/fetch.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/installed.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/plugin.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/source.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/types.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/agent_profile.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/agent_profile_store.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/profile_refs.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/resolver.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/seed.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/py.typed +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/secret/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/secret/secrets.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/_shell_ast.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/analyzer.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/confirmation_policy.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/ensemble.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/grayswan/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/grayswan/analyzer.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/grayswan/utils.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/llm_analyzer.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/risk.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/shell_parser.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/toolshield_helpers.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/acp_install_catalog.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/acp_providers.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/api_models.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/metadata.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/model.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/exceptions.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/execute.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/fetch.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/installed.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/trigger.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/types.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/subagent/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/subagent/registry.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/subagent/schema.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/testing/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/testing/test_llm.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/finish.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/think.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/client_tool.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/defaults.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/registry.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/schema.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/spec.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/tool.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/async_executor.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/async_utils.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/cipher.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/command.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/datetime.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/deprecation.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/files.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/github.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/json.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/models.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/paging.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/pydantic_secrets.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/redact.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/truncate.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/visualize.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/local.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/models.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/remote/__init__.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/remote/base.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/repo.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/workspace.py +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands_sdk.egg-info/dependency_links.txt +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands_sdk.egg-info/top_level.txt +0 -0
- {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-sdk
|
|
3
|
-
Version: 1.49.
|
|
3
|
+
Version: 1.49.2
|
|
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
|
|
@@ -10,7 +10,7 @@ Requires-Python: >=3.12
|
|
|
10
10
|
Requires-Dist: agent-client-protocol<0.11.0,>=0.10.1
|
|
11
11
|
Requires-Dist: deprecation>=2.1.0
|
|
12
12
|
Requires-Dist: fakeredis[lua]>=2.32.1
|
|
13
|
-
Requires-Dist: fastmcp
|
|
13
|
+
Requires-Dist: fastmcp<4,>=3.2.0
|
|
14
14
|
Requires-Dist: filelock>=3.20.1
|
|
15
15
|
Requires-Dist: httpx[socks]>=0.27.0
|
|
16
16
|
Requires-Dist: jsonschema>=4.23.0
|
{openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/impl/local_conversation.py
RENAMED
|
@@ -63,8 +63,6 @@ from openhands.sdk.marketplace.registry import MarketplaceRegistry
|
|
|
63
63
|
from openhands.sdk.mcp.client import MCPClient
|
|
64
64
|
from openhands.sdk.mcp.config import (
|
|
65
65
|
MCPServer,
|
|
66
|
-
coerce_mcp_config,
|
|
67
|
-
dump_mcp_config,
|
|
68
66
|
enabled_mcp_servers,
|
|
69
67
|
)
|
|
70
68
|
from openhands.sdk.mcp.tool import MCPToolDefinition
|
|
@@ -100,7 +98,7 @@ from openhands.sdk.skills import (
|
|
|
100
98
|
merge_skills_by_name,
|
|
101
99
|
)
|
|
102
100
|
from openhands.sdk.skills.utils import (
|
|
103
|
-
|
|
101
|
+
expand_mcp_servers,
|
|
104
102
|
expand_variable_references,
|
|
105
103
|
)
|
|
106
104
|
from openhands.sdk.subagent import (
|
|
@@ -1205,16 +1203,13 @@ class LocalConversation(BaseConversation):
|
|
|
1205
1203
|
# - Variables with defaults that don't have secrets fall back to their defaults
|
|
1206
1204
|
# - This is the ONLY place where defaults are applied (plugin loading preserves
|
|
1207
1205
|
# placeholders with expand_defaults=False to avoid double-expansion)
|
|
1206
|
+
# Agent Plugins servers are left literal (see expand_mcp_servers).
|
|
1208
1207
|
if merged_mcp:
|
|
1209
1208
|
# Pass the registry's lookup method as a callback - secrets are retrieved
|
|
1210
1209
|
# lazily, one at a time, only when actually referenced in the config
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
{},
|
|
1214
|
-
get_secret=self._state.secret_registry.get_secret_value,
|
|
1215
|
-
expand_defaults=True,
|
|
1210
|
+
merged_mcp = expand_mcp_servers(
|
|
1211
|
+
merged_mcp, self._state.secret_registry.get_secret_value
|
|
1216
1212
|
)
|
|
1217
|
-
merged_mcp = coerce_mcp_config(expanded_mcp["mcpServers"])
|
|
1218
1213
|
logger.debug("Expanded MCP config variables")
|
|
1219
1214
|
|
|
1220
1215
|
# Update agent with merged content only if something changed.
|
|
@@ -1447,25 +1442,13 @@ class LocalConversation(BaseConversation):
|
|
|
1447
1442
|
get_secret = self._state.secret_registry.get_secret_value
|
|
1448
1443
|
runtime_plugin_mcp: dict[str, MCPServer] = {}
|
|
1449
1444
|
if plugin.mcp_config:
|
|
1450
|
-
|
|
1451
|
-
{"mcpServers": dump_mcp_config(plugin.mcp_config)},
|
|
1452
|
-
{},
|
|
1453
|
-
get_secret=get_secret,
|
|
1454
|
-
expand_defaults=True,
|
|
1455
|
-
)
|
|
1456
|
-
runtime_plugin_mcp = coerce_mcp_config(expanded_plugin_mcp["mcpServers"])
|
|
1445
|
+
runtime_plugin_mcp = expand_mcp_servers(plugin.mcp_config, get_secret)
|
|
1457
1446
|
merged_context = plugin.add_skills_to(self.agent.agent_context)
|
|
1458
1447
|
merged_mcp = plugin.add_mcp_config_to(
|
|
1459
1448
|
dict(self.agent.mcp_config) if self.agent.mcp_config else {}
|
|
1460
1449
|
)
|
|
1461
1450
|
if merged_mcp:
|
|
1462
|
-
|
|
1463
|
-
{"mcpServers": dump_mcp_config(merged_mcp)},
|
|
1464
|
-
{},
|
|
1465
|
-
get_secret=get_secret,
|
|
1466
|
-
expand_defaults=True,
|
|
1467
|
-
)
|
|
1468
|
-
merged_mcp = coerce_mcp_config(expanded_mcp["mcpServers"])
|
|
1451
|
+
merged_mcp = expand_mcp_servers(merged_mcp, get_secret)
|
|
1469
1452
|
runtime_mcp_tools = (
|
|
1470
1453
|
self._runtime_mcp_tools(
|
|
1471
1454
|
runtime_plugin_mcp,
|
|
@@ -14,6 +14,7 @@ from pydantic import (
|
|
|
14
14
|
Field,
|
|
15
15
|
GetCoreSchemaHandler,
|
|
16
16
|
GetJsonSchemaHandler,
|
|
17
|
+
PrivateAttr,
|
|
17
18
|
SecretStr,
|
|
18
19
|
SerializationInfo,
|
|
19
20
|
TypeAdapter,
|
|
@@ -520,6 +521,9 @@ class MCPServer(_MCPBaseModel):
|
|
|
520
521
|
"ACP subprocess."
|
|
521
522
|
),
|
|
522
523
|
)
|
|
524
|
+
# Never a field: data from outside must not be able to exempt a server from
|
|
525
|
+
# expansion. Only code sets it, via ``as_literal()``.
|
|
526
|
+
_literal_values: bool = PrivateAttr(default=False)
|
|
523
527
|
|
|
524
528
|
@field_validator("env", "headers", mode="after")
|
|
525
529
|
@classmethod
|
|
@@ -554,6 +558,20 @@ class MCPServer(_MCPBaseModel):
|
|
|
554
558
|
)
|
|
555
559
|
return self
|
|
556
560
|
|
|
561
|
+
@property
|
|
562
|
+
def literal_values(self) -> bool:
|
|
563
|
+
"""True if built by :meth:`as_literal`: never ``${VAR}``-expanded.
|
|
564
|
+
|
|
565
|
+
In memory only; input cannot set it and dumps do not write it.
|
|
566
|
+
"""
|
|
567
|
+
return self._literal_values
|
|
568
|
+
|
|
569
|
+
def as_literal(self) -> MCPServer:
|
|
570
|
+
"""Return a copy exempt from ``${VAR}`` expansion, for package servers."""
|
|
571
|
+
server = self.model_copy()
|
|
572
|
+
server._literal_values = True
|
|
573
|
+
return server
|
|
574
|
+
|
|
557
575
|
@property
|
|
558
576
|
def effective_transport(self) -> MCPTransport | None:
|
|
559
577
|
return self.transport
|
|
@@ -6,12 +6,15 @@ import logging
|
|
|
6
6
|
from collections.abc import Callable, Mapping, Sequence
|
|
7
7
|
from typing import Protocol
|
|
8
8
|
|
|
9
|
+
import httpx
|
|
9
10
|
import mcp.types
|
|
10
11
|
from fastmcp.client.auth import OAuth
|
|
11
12
|
from fastmcp.client.logging import LogMessage
|
|
12
13
|
from fastmcp.client.messages import MessageHandler
|
|
14
|
+
from fastmcp.client.transports import StreamableHttpTransport
|
|
13
15
|
from fastmcp.mcp_config import MCPConfig as FastMCPConfig, RemoteMCPServer
|
|
14
16
|
from key_value.aio.protocols import AsyncKeyValue
|
|
17
|
+
from mcp.shared._httpx_utils import create_mcp_http_client
|
|
15
18
|
|
|
16
19
|
from openhands.sdk.logger import get_logger
|
|
17
20
|
from openhands.sdk.mcp.client import MCPClient, ToolsReconciledCallback
|
|
@@ -114,6 +117,52 @@ def _oauth_auth_from_authentication_config(
|
|
|
114
117
|
)
|
|
115
118
|
|
|
116
119
|
|
|
120
|
+
class _PackageRemoteMCPServer(RemoteMCPServer):
|
|
121
|
+
"""A package-declared remote server whose headers stay on its own origin.
|
|
122
|
+
|
|
123
|
+
Agent Plugins §7.2.1: configured headers must not follow a redirect to a
|
|
124
|
+
different origin. httpx strips only ``Authorization`` there, so this
|
|
125
|
+
server's HTTP client drops every configured header name from any request
|
|
126
|
+
that leaves the configured origin.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
def to_transport(self): # type: ignore[override]
|
|
130
|
+
transport = super().to_transport()
|
|
131
|
+
if isinstance(transport, StreamableHttpTransport) and self.headers:
|
|
132
|
+
transport.httpx_client_factory = _origin_bound_client_factory(
|
|
133
|
+
self.url, tuple(self.headers)
|
|
134
|
+
)
|
|
135
|
+
return transport
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _origin_bound_client_factory(url: str, header_names: tuple[str, ...]):
|
|
139
|
+
origin = _origin(httpx.URL(url))
|
|
140
|
+
|
|
141
|
+
async def drop_headers_off_origin(request: httpx.Request) -> None:
|
|
142
|
+
# Request hooks run on every redirect hop, after httpx has copied the
|
|
143
|
+
# previous request's headers onto the next one.
|
|
144
|
+
if _origin(request.url) != origin:
|
|
145
|
+
for name in header_names:
|
|
146
|
+
request.headers.pop(name, None)
|
|
147
|
+
|
|
148
|
+
def factory(
|
|
149
|
+
headers: dict[str, str] | None = None,
|
|
150
|
+
timeout: httpx.Timeout | None = None,
|
|
151
|
+
auth: httpx.Auth | None = None,
|
|
152
|
+
**_: object,
|
|
153
|
+
) -> httpx.AsyncClient:
|
|
154
|
+
client = create_mcp_http_client(headers=headers, timeout=timeout, auth=auth)
|
|
155
|
+
client.event_hooks = {"request": [drop_headers_off_origin], "response": []}
|
|
156
|
+
return client
|
|
157
|
+
|
|
158
|
+
return factory
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _origin(url: httpx.URL) -> tuple[str, str, int | None]:
|
|
162
|
+
default_port = {"http": 80, "https": 443}.get(url.scheme)
|
|
163
|
+
return url.scheme, url.host, url.port or default_port
|
|
164
|
+
|
|
165
|
+
|
|
117
166
|
def _prepare_mcp_config(
|
|
118
167
|
mcp_config: dict[str, MCPServer],
|
|
119
168
|
*,
|
|
@@ -123,6 +172,13 @@ def _prepare_mcp_config(
|
|
|
123
172
|
"""Validate MCP config and apply explicit OpenHands runtime auth metadata."""
|
|
124
173
|
prepared = FastMCPConfig.model_validate(to_fastmcp_mcp_config(mcp_config))
|
|
125
174
|
|
|
175
|
+
for server_name, server_spec in mcp_config.items():
|
|
176
|
+
server = prepared.mcpServers.get(server_name)
|
|
177
|
+
if server_spec.literal_values and isinstance(server, RemoteMCPServer):
|
|
178
|
+
prepared.mcpServers[server_name] = _PackageRemoteMCPServer.model_validate(
|
|
179
|
+
server.model_dump()
|
|
180
|
+
)
|
|
181
|
+
|
|
126
182
|
for server_name, server_spec in mcp_config.items():
|
|
127
183
|
auth = server_spec.auth
|
|
128
184
|
if not isinstance(auth, MCPOAuthAuthCredential):
|
|
@@ -27,6 +27,7 @@ from openhands.sdk.plugin.format import (
|
|
|
27
27
|
PluginFormat,
|
|
28
28
|
detect_format,
|
|
29
29
|
)
|
|
30
|
+
from openhands.sdk.plugin.format.agent_plugins_mcp import get_plugin_data_dir
|
|
30
31
|
from openhands.sdk.plugin.installed import (
|
|
31
32
|
InstalledPluginInfo,
|
|
32
33
|
disable_plugin,
|
|
@@ -92,6 +93,7 @@ __all__ = [
|
|
|
92
93
|
"load_installed_plugins",
|
|
93
94
|
"get_installed_plugins_dir",
|
|
94
95
|
"get_installed_plugin",
|
|
96
|
+
"get_plugin_data_dir",
|
|
95
97
|
"enable_plugin",
|
|
96
98
|
"disable_plugin",
|
|
97
99
|
"update_plugin",
|
|
@@ -12,19 +12,16 @@ Module layout:
|
|
|
12
12
|
- ``base`` — the abstract :class:`PluginFormat` contract plus the shared
|
|
13
13
|
discovery logic (skills discovery, final assembly).
|
|
14
14
|
- ``claude_code`` — the concrete :class:`ClaudeCodePluginFormat` strategy.
|
|
15
|
-
- ``agent_plugins`` — the :class:`AgentPluginsFormat` strategy,
|
|
16
|
-
|
|
15
|
+
- ``agent_plugins`` — the :class:`AgentPluginsFormat` strategy, which also owns
|
|
16
|
+
our ``dev.openhands`` client-extension namespace, and ``agent_plugins_mcp``,
|
|
17
|
+
its ``mcp.json`` loader.
|
|
17
18
|
- this package ``__init__`` — the format registry (``_FORMATS``) and the
|
|
18
19
|
:func:`detect_format` dispatcher.
|
|
19
20
|
|
|
20
21
|
:func:`detect_format` returns the first format in ``_FORMATS`` whose
|
|
21
|
-
:meth:`PluginFormat.detect` returns True.
|
|
22
|
-
|
|
23
|
-
fallback.
|
|
24
|
-
|
|
25
|
-
:class:`AgentPluginsFormat` is held out of ``_FORMATS`` until its ``mcp.json``
|
|
26
|
-
loader lands (#4405): registering it earlier would claim any directory with a
|
|
27
|
-
root ``plugin.json`` and load it with zero MCP servers.
|
|
22
|
+
:meth:`PluginFormat.detect` returns True. Agent Plugins goes first because it is
|
|
23
|
+
specific — a root ``plugin.json`` — and Claude Code last because its ``detect``
|
|
24
|
+
accepts any directory, making it the universal fallback.
|
|
28
25
|
|
|
29
26
|
How to add a new plugin format
|
|
30
27
|
------------------------------
|
|
@@ -62,9 +59,7 @@ logger = get_logger(__name__)
|
|
|
62
59
|
|
|
63
60
|
# Registered formats, in detection-precedence order. Higher-precedence formats
|
|
64
61
|
# come first; the Claude Code format is last because it accepts any directory.
|
|
65
|
-
|
|
66
|
-
# (see the module docstring); it belongs ahead of ClaudeCodePluginFormat.
|
|
67
|
-
_FORMATS: list[type[PluginFormat]] = [ClaudeCodePluginFormat]
|
|
62
|
+
_FORMATS: list[type[PluginFormat]] = [AgentPluginsFormat, ClaudeCodePluginFormat]
|
|
68
63
|
|
|
69
64
|
|
|
70
65
|
def detect_format(plugin_dir: Path) -> PluginFormat:
|
|
@@ -8,7 +8,6 @@ See the ``openhands.sdk.plugin.format`` package docstring for the design.
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import json
|
|
11
|
-
from functools import cache
|
|
12
11
|
from pathlib import Path
|
|
13
12
|
from typing import Any, ClassVar, Final
|
|
14
13
|
|
|
@@ -18,14 +17,20 @@ from jsonschema.exceptions import ValidationError as JSONSchemaValidationError
|
|
|
18
17
|
from openhands.sdk.hooks import HookConfig
|
|
19
18
|
from openhands.sdk.logger import get_logger
|
|
20
19
|
from openhands.sdk.mcp.config import MCPServer
|
|
20
|
+
from openhands.sdk.plugin.format.agent_plugins_mcp import (
|
|
21
|
+
get_plugin_data_dir,
|
|
22
|
+
load_mcp_servers,
|
|
23
|
+
)
|
|
21
24
|
from openhands.sdk.plugin.format.base import (
|
|
22
25
|
PluginFormat,
|
|
26
|
+
_load_schema,
|
|
23
27
|
_read_command_definitions,
|
|
24
28
|
_read_hooks_config,
|
|
25
29
|
)
|
|
26
30
|
from openhands.sdk.plugin.types import CommandDefinition, PluginManifest
|
|
27
31
|
from openhands.sdk.subagent.load import load_agents_from_dir
|
|
28
32
|
from openhands.sdk.subagent.schema import AgentDefinition
|
|
33
|
+
from openhands.sdk.utils.path import resolves_within
|
|
29
34
|
|
|
30
35
|
|
|
31
36
|
logger = get_logger(__name__)
|
|
@@ -39,7 +44,6 @@ MANIFEST_FILE: Final[str] = "plugin.json"
|
|
|
39
44
|
#: components (§8.2).
|
|
40
45
|
EXTENSION_NAMESPACE: Final[str] = "dev.openhands"
|
|
41
46
|
|
|
42
|
-
_SCHEMAS_DIR: Final[Path] = Path(__file__).parent / "schemas"
|
|
43
47
|
|
|
44
48
|
#: The only manifest ``$schema`` we support, and its vendored file. Agent
|
|
45
49
|
#: Plugins also publishes an ``mcp.schema.json``, but that one belongs to the
|
|
@@ -51,12 +55,6 @@ MANIFEST_SCHEMA_URL: Final[str] = (
|
|
|
51
55
|
_MANIFEST_SCHEMA_FILE: Final[str] = "plugin-1.0.0.schema.json"
|
|
52
56
|
|
|
53
57
|
|
|
54
|
-
@cache
|
|
55
|
-
def _load_schema(filename: str) -> dict[str, Any]:
|
|
56
|
-
"""Read a vendored schema. Cached; never fetched over the network."""
|
|
57
|
-
return json.loads((_SCHEMAS_DIR / filename).read_text(encoding="utf-8"))
|
|
58
|
-
|
|
59
|
-
|
|
60
58
|
class AgentPluginsFormat(PluginFormat):
|
|
61
59
|
"""The Agent Plugins v1.0.0 portable package layout.
|
|
62
60
|
|
|
@@ -79,13 +77,17 @@ class AgentPluginsFormat(PluginFormat):
|
|
|
79
77
|
moving those directories rather than rewriting them. Namespaces other than
|
|
80
78
|
ours are ignored without validating their contents.
|
|
81
79
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
Args:
|
|
81
|
+
plugin_data_root: Parent of the per-plugin ``PLUGIN_DATA`` directories.
|
|
82
|
+
Defaults to the user's plugin data directory; injectable for tests
|
|
83
|
+
and for callers that keep plugin state somewhere else.
|
|
85
84
|
"""
|
|
86
85
|
|
|
87
86
|
name: ClassVar[str] = "agent-plugins"
|
|
88
87
|
|
|
88
|
+
def __init__(self, *, plugin_data_root: Path | None = None) -> None:
|
|
89
|
+
self._plugin_data_root = plugin_data_root
|
|
90
|
+
|
|
89
91
|
@classmethod
|
|
90
92
|
def detect(cls, plugin_dir: Path) -> bool:
|
|
91
93
|
# Presence is the whole rule: a broken manifest (bad JSON, missing or
|
|
@@ -111,6 +113,10 @@ class AgentPluginsFormat(PluginFormat):
|
|
|
111
113
|
ValueError: On any fatal manifest violation.
|
|
112
114
|
"""
|
|
113
115
|
manifest_path = plugin_dir / MANIFEST_FILE
|
|
116
|
+
if not resolves_within(manifest_path, plugin_dir):
|
|
117
|
+
raise ValueError(
|
|
118
|
+
f"Manifest {manifest_path} resolves outside the plugin root"
|
|
119
|
+
)
|
|
114
120
|
try:
|
|
115
121
|
# utf-8-sig: tolerate a leading BOM, which RFC 8259 lets us ignore.
|
|
116
122
|
data = json.loads(manifest_path.read_text(encoding="utf-8-sig"))
|
|
@@ -161,21 +167,35 @@ class AgentPluginsFormat(PluginFormat):
|
|
|
161
167
|
data | _extension_manifest_fields(data, manifest_path)
|
|
162
168
|
)
|
|
163
169
|
|
|
164
|
-
def load_mcp_config(self, plugin_dir: Path) -> dict[str, MCPServer]:
|
|
165
|
-
"""
|
|
166
|
-
|
|
170
|
+
def load_mcp_config(self, plugin_dir: Path) -> dict[str, MCPServer]:
|
|
171
|
+
"""Load MCP servers from the root ``mcp.json`` (no leading dot).
|
|
172
|
+
|
|
173
|
+
Unlike the Claude Code format, expansion is complete here: Agent Plugins
|
|
174
|
+
defines exactly two placeholders and forbids every other kind, so there
|
|
175
|
+
is nothing left to expand once per-conversation secrets arrive.
|
|
176
|
+
"""
|
|
177
|
+
plugin_root = plugin_dir.resolve()
|
|
178
|
+
return load_mcp_servers(
|
|
179
|
+
plugin_dir,
|
|
180
|
+
plugin_root=plugin_root,
|
|
181
|
+
plugin_data=get_plugin_data_dir(
|
|
182
|
+
plugin_root, data_root=self._plugin_data_root
|
|
183
|
+
),
|
|
184
|
+
)
|
|
167
185
|
|
|
168
186
|
def load_hooks(self, plugin_dir: Path) -> HookConfig | None:
|
|
169
187
|
"""Load hooks from ``dev.openhands/hooks/hooks.json``."""
|
|
170
|
-
return _read_hooks_config(plugin_dir / EXTENSION_NAMESPACE)
|
|
188
|
+
return _read_hooks_config(plugin_dir / EXTENSION_NAMESPACE, plugin_dir)
|
|
171
189
|
|
|
172
190
|
def load_agents(self, plugin_dir: Path) -> list[AgentDefinition]:
|
|
173
191
|
"""Load agent definitions from ``dev.openhands/agents/``."""
|
|
174
|
-
return load_agents_from_dir(
|
|
192
|
+
return load_agents_from_dir(
|
|
193
|
+
plugin_dir / EXTENSION_NAMESPACE / "agents", root=plugin_dir
|
|
194
|
+
)
|
|
175
195
|
|
|
176
196
|
def load_commands(self, plugin_dir: Path) -> list[CommandDefinition]:
|
|
177
197
|
"""Load command definitions from ``dev.openhands/commands/``."""
|
|
178
|
-
return _read_command_definitions(plugin_dir / EXTENSION_NAMESPACE)
|
|
198
|
+
return _read_command_definitions(plugin_dir / EXTENSION_NAMESPACE, plugin_dir)
|
|
179
199
|
|
|
180
200
|
|
|
181
201
|
def _extension_manifest_fields(
|