From d78dd7e933d8c17476b7a43cf14a16d72c855f27 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 11 May 2026 15:17:04 +0000 Subject: [PATCH 1/4] Release v1.22.0 Co-authored-by: openhands --- .github/workflows/run-eval.yml | 3 ++- openhands-agent-server/pyproject.toml | 2 +- openhands-sdk/pyproject.toml | 2 +- openhands-tools/pyproject.toml | 2 +- openhands-workspace/pyproject.toml | 2 +- uv.lock | 8 ++++---- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/run-eval.yml b/.github/workflows/run-eval.yml index 3610850d3b..ee61111dd7 100644 --- a/.github/workflows/run-eval.yml +++ b/.github/workflows/run-eval.yml @@ -24,7 +24,8 @@ on: sdk_ref: description: SDK commit/ref to evaluate (must be a semantic version like v1.0.0 unless 'Allow unreleased branches' is checked) required: true - default: v1.21.1 + default: v1.22.0 + diff --git a/openhands-agent-server/pyproject.toml b/openhands-agent-server/pyproject.toml index cad9c20d27..465026555d 100644 --- a/openhands-agent-server/pyproject.toml +++ b/openhands-agent-server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openhands-agent-server" -version = "1.21.1" +version = "1.22.0" description = "OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent" requires-python = ">=3.12" diff --git a/openhands-sdk/pyproject.toml b/openhands-sdk/pyproject.toml index 9ca6c4bb1a..393d7de04b 100644 --- a/openhands-sdk/pyproject.toml +++ b/openhands-sdk/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openhands-sdk" -version = "1.21.1" +version = "1.22.0" description = "OpenHands SDK - Core functionality for building AI agents" requires-python = ">=3.12" diff --git a/openhands-tools/pyproject.toml b/openhands-tools/pyproject.toml index 0afa4da82d..3ec843a8c2 100644 --- a/openhands-tools/pyproject.toml +++ b/openhands-tools/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openhands-tools" -version = "1.21.1" +version = "1.22.0" description = "OpenHands Tools - Runtime tools for AI agents" requires-python = ">=3.12" diff --git a/openhands-workspace/pyproject.toml b/openhands-workspace/pyproject.toml index 8bfde8ad19..a1d83c45f9 100644 --- a/openhands-workspace/pyproject.toml +++ b/openhands-workspace/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openhands-workspace" -version = "1.21.1" +version = "1.22.0" description = "OpenHands Workspace - Docker and container-based workspace implementations" requires-python = ">=3.12" diff --git a/uv.lock b/uv.lock index 753d1aca8c..616f4cca9f 100644 --- a/uv.lock +++ b/uv.lock @@ -2463,7 +2463,7 @@ wheels = [ [[package]] name = "openhands-agent-server" -version = "1.21.1" +version = "1.22.0" source = { editable = "openhands-agent-server" } dependencies = [ { name = "aiosqlite" }, @@ -2494,7 +2494,7 @@ requires-dist = [ [[package]] name = "openhands-sdk" -version = "1.21.1" +version = "1.22.0" source = { editable = "openhands-sdk" } dependencies = [ { name = "agent-client-protocol" }, @@ -2542,7 +2542,7 @@ provides-extras = ["boto3"] [[package]] name = "openhands-tools" -version = "1.21.1" +version = "1.22.0" source = { editable = "openhands-tools" } dependencies = [ { name = "bashlex" }, @@ -2571,7 +2571,7 @@ requires-dist = [ [[package]] name = "openhands-workspace" -version = "1.21.1" +version = "1.22.0" source = { editable = "openhands-workspace" } dependencies = [ { name = "openhands-agent-server" }, From e69980f02e56d3a185596afa791f7768f886c5cf Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 11 May 2026 15:41:50 +0000 Subject: [PATCH 2/4] Update settings deprecations for release Co-authored-by: openhands --- openhands-sdk/openhands/sdk/__init__.py | 2 +- .../openhands/sdk/settings/__init__.py | 2 +- openhands-sdk/openhands/sdk/settings/model.py | 84 +++++-------------- tests/sdk/test_settings.py | 35 ++++++-- 4 files changed, 49 insertions(+), 74 deletions(-) diff --git a/openhands-sdk/openhands/sdk/__init__.py b/openhands-sdk/openhands/sdk/__init__.py index f3632c7522..1c2dafabf2 100644 --- a/openhands-sdk/openhands/sdk/__init__.py +++ b/openhands-sdk/openhands/sdk/__init__.py @@ -121,7 +121,7 @@ _DEPRECATED_SDK_EXPORTS: dict[str, dict[str, str]] = { "LLMAgentSettings": { "deprecated_in": "1.19.0", - "removed_in": "1.22.0", + "removed_in": "1.23.0", "details": ( "Use ``OpenHandsAgentSettings`` directly. " "``LLMAgentSettings`` was renamed in v1.19.0." diff --git a/openhands-sdk/openhands/sdk/settings/__init__.py b/openhands-sdk/openhands/sdk/settings/__init__.py index 04c6df60ed..f2649e522a 100644 --- a/openhands-sdk/openhands/sdk/settings/__init__.py +++ b/openhands-sdk/openhands/sdk/settings/__init__.py @@ -123,7 +123,7 @@ def __getattr__(name: str) -> Any: warn_deprecated( f"Importing {name!r} from openhands.sdk.settings", deprecated_in="1.19.0", - removed_in="1.22.0", + removed_in="1.23.0", details=( "Use ``OpenHandsAgentSettings`` directly. " "``LLMAgentSettings`` was renamed in v1.19.0." diff --git a/openhands-sdk/openhands/sdk/settings/model.py b/openhands-sdk/openhands/sdk/settings/model.py index 61a7bba697..3c238e7195 100644 --- a/openhands-sdk/openhands/sdk/settings/model.py +++ b/openhands-sdk/openhands/sdk/settings/model.py @@ -230,65 +230,6 @@ class VerificationSettings(BaseModel): }, ) - # -- Deprecated (moved to ConversationSettings) -- - confirmation_mode: bool = Field( - default=False, - description="Require user confirmation before executing risky actions.", - deprecated=( - "Deprecated in 1.17.0; use ConversationSettings.confirmation_mode " - "instead. Will be removed in 1.22.0." - ), - json_schema_extra={ - SETTINGS_METADATA_KEY: SettingsFieldMetadata( - label="Confirmation mode", - prominence=SettingProminence.MAJOR, - ).model_dump() - }, - ) - security_analyzer: SecurityAnalyzerType | None = Field( - default=None, - description=("Security analyzer that evaluates actions before execution."), - deprecated=( - "Deprecated in 1.17.0; use ConversationSettings.security_analyzer " - "instead. Will be removed in 1.22.0." - ), - json_schema_extra={ - SETTINGS_METADATA_KEY: SettingsFieldMetadata( - label="Security analyzer", - prominence=SettingProminence.MAJOR, - depends_on=("confirmation_mode",), - ).model_dump() - }, - ) - - @field_validator("confirmation_mode", mode="before") - @classmethod - def _warn_confirmation_mode(cls, v: Any) -> Any: - if v: - from openhands.sdk.utils.deprecation import warn_deprecated - - warn_deprecated( - "VerificationSettings.confirmation_mode", - deprecated_in="1.17.0", - removed_in="1.22.0", - details="Use ConversationSettings.confirmation_mode instead.", - ) - return v - - @field_validator("security_analyzer", mode="before") - @classmethod - def _warn_security_analyzer(cls, v: Any) -> Any: - if v is not None: - from openhands.sdk.utils.deprecation import warn_deprecated - - warn_deprecated( - "VerificationSettings.security_analyzer", - deprecated_in="1.17.0", - removed_in="1.22.0", - details="Use ConversationSettings.security_analyzer instead.", - ) - return v - def _default_llm_settings() -> LLM: model = LLM.model_fields["model"].get_default() @@ -298,7 +239,7 @@ def _default_llm_settings() -> LLM: _RequestT = TypeVar("_RequestT") -AGENT_SETTINGS_SCHEMA_VERSION = 2 +AGENT_SETTINGS_SCHEMA_VERSION = 3 CONVERSATION_SETTINGS_SCHEMA_VERSION = 1 @@ -420,7 +361,7 @@ def _migrate_agent_settings_v1_to_v2(payload: dict[str, Any]) -> dict[str, Any]: persisted payloads carried ``agent_kind: 'llm'``. The two classes are field-compatible (``LLMAgentSettings`` is a subclass of ``OpenHandsAgentSettings`` that only narrows the discriminator literal), - and ``LLMAgentSettings`` is scheduled for removal in v1.22.0. Rewriting + and ``LLMAgentSettings`` is scheduled for removal in v1.23.0. Rewriting the discriminator on read lets callers that explicitly validate as ``OpenHandsAgentSettings`` (the canonical class) accept legacy data without losing any fields. @@ -432,6 +373,19 @@ def _migrate_agent_settings_v1_to_v2(payload: dict[str, Any]) -> dict[str, Any]: return migrated +def _migrate_agent_settings_v2_to_v3(payload: dict[str, Any]) -> dict[str, Any]: + """Drop deprecated verification fields moved to ``ConversationSettings``.""" + migrated = dict(payload) + verification = migrated.get("verification") + if isinstance(verification, Mapping): + verification = dict(verification) + verification.pop("confirmation_mode", None) + verification.pop("security_analyzer", None) + migrated["verification"] = verification + migrated["schema_version"] = 3 + return migrated + + def _migrate_conversation_settings_v0_to_v1( payload: dict[str, Any], ) -> dict[str, Any]: @@ -443,6 +397,7 @@ def _migrate_conversation_settings_v0_to_v1( _AGENT_SETTINGS_MIGRATIONS: dict[int, PersistedSettingsMigrator] = { 0: _migrate_agent_settings_v0_to_v1, 1: _migrate_agent_settings_v1_to_v2, + 2: _migrate_agent_settings_v2_to_v3, } _CONVERSATION_SETTINGS_MIGRATIONS: dict[int, PersistedSettingsMigrator] = { 0: _migrate_conversation_settings_v0_to_v1, @@ -1177,7 +1132,7 @@ class LLMAgentSettings(OpenHandsAgentSettings): Use :class:`OpenHandsAgentSettings` for all new code. - Scheduled for removal in v1.22.0. + Scheduled for removal in v1.23.0. """ # Keep agent_kind as Literal["llm"] so the API-breakage checker sees no @@ -1273,8 +1228,7 @@ class AgentSettings(LLMAgentSettings): * Use :func:`validate_agent_settings` to validate raw payloads into the correct variant. - Scheduled for removal in v1.22.0 (5 minor releases after the - discriminated-union landing in v1.17.1). + Scheduled for removal in v1.23.0. """ @classmethod @@ -1296,7 +1250,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: warn_deprecated( "AgentSettings", deprecated_in="1.17.0", - removed_in="1.22.0", + removed_in="1.23.0", details=( "Use ``OpenHandsAgentSettings`` (for an LLM agent) or " "``ACPAgentSettings`` (for an ACP agent) directly; use " diff --git a/tests/sdk/test_settings.py b/tests/sdk/test_settings.py index 4d2205ac29..4e254a1e3b 100644 --- a/tests/sdk/test_settings.py +++ b/tests/sdk/test_settings.py @@ -352,7 +352,7 @@ def test_agent_settings_from_persisted_migrates_v0_llm_payload() -> None: settings = AgentSettings.from_persisted({"llm": {"model": "test-model"}}) assert isinstance(settings, OpenHandsAgentSettings) - assert settings.schema_version == 2 + assert settings.schema_version == 3 assert settings.agent_kind == "openhands" assert settings.llm.model == "test-model" @@ -368,8 +368,8 @@ def test_agent_settings_from_persisted_dispatches_current_acp_payload() -> None: ) assert isinstance(settings, ACPAgentSettings) - # v1 → v2 is a no-op for ACP payloads, but the schema_version is bumped. - assert settings.schema_version == 2 + # v1 → v2 → v3 keeps ACP payloads intact while bumping schema_version. + assert settings.schema_version == 3 assert settings.acp_command == ["npx", "-y", "claude-agent-acp"] @@ -385,14 +385,35 @@ def test_agent_settings_from_persisted_canonicalizes_legacy_llm_kind() -> None: ) assert isinstance(settings, OpenHandsAgentSettings) - assert settings.schema_version == 2 + assert settings.schema_version == 3 assert settings.agent_kind == "openhands" assert settings.llm.model == "legacy-model" +def test_agent_settings_from_persisted_drops_legacy_verification_fields() -> None: + settings = AgentSettings.from_persisted( + { + "schema_version": 2, + "agent_kind": "openhands", + "verification": { + "critic_enabled": True, + "confirmation_mode": True, + "security_analyzer": "llm", + }, + } + ) + + assert isinstance(settings, OpenHandsAgentSettings) + assert settings.schema_version == 3 + verification = settings.verification.model_dump(mode="json") + assert verification["critic_enabled"] is True + assert "confirmation_mode" not in verification + assert "security_analyzer" not in verification + + def test_agent_settings_from_persisted_rejects_newer_schema_version() -> None: - with pytest.raises(ValueError, match="newer than supported version 2"): - AgentSettings.from_persisted({"schema_version": 3, "llm": {"model": "m"}}) + with pytest.raises(ValueError, match="newer than supported version 3"): + AgentSettings.from_persisted({"schema_version": 4, "llm": {"model": "m"}}) def test_conversation_settings_from_persisted_migrates_v0_payload() -> None: @@ -653,7 +674,7 @@ def test_legacy_agent_settings_still_instantiates_as_llm_variant() -> None: settings = AgentSettings(llm=LLM(model="test-model")) # The legacy name emits a DeprecationWarning on construction. The - # warning's scheduled removal is in 1.22.0 per the class docstring. + # warning's scheduled removal is in 1.23.0 per the class docstring. assert any("AgentSettings" in str(w.message) for w in caught), ( f"expected deprecation warning, got: {[str(w.message) for w in caught]}" ) From 2786c3890304f02969361980661de660464e38e1 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 11 May 2026 16:29:36 +0000 Subject: [PATCH 3/4] Correct LLMAgentSettings removal deadline Co-authored-by: openhands --- openhands-sdk/openhands/sdk/__init__.py | 2 +- openhands-sdk/openhands/sdk/settings/__init__.py | 2 +- openhands-sdk/openhands/sdk/settings/model.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openhands-sdk/openhands/sdk/__init__.py b/openhands-sdk/openhands/sdk/__init__.py index 1c2dafabf2..6064bb163b 100644 --- a/openhands-sdk/openhands/sdk/__init__.py +++ b/openhands-sdk/openhands/sdk/__init__.py @@ -121,7 +121,7 @@ _DEPRECATED_SDK_EXPORTS: dict[str, dict[str, str]] = { "LLMAgentSettings": { "deprecated_in": "1.19.0", - "removed_in": "1.23.0", + "removed_in": "1.24.0", "details": ( "Use ``OpenHandsAgentSettings`` directly. " "``LLMAgentSettings`` was renamed in v1.19.0." diff --git a/openhands-sdk/openhands/sdk/settings/__init__.py b/openhands-sdk/openhands/sdk/settings/__init__.py index f2649e522a..1f6b33db66 100644 --- a/openhands-sdk/openhands/sdk/settings/__init__.py +++ b/openhands-sdk/openhands/sdk/settings/__init__.py @@ -123,7 +123,7 @@ def __getattr__(name: str) -> Any: warn_deprecated( f"Importing {name!r} from openhands.sdk.settings", deprecated_in="1.19.0", - removed_in="1.23.0", + removed_in="1.24.0", details=( "Use ``OpenHandsAgentSettings`` directly. " "``LLMAgentSettings`` was renamed in v1.19.0." diff --git a/openhands-sdk/openhands/sdk/settings/model.py b/openhands-sdk/openhands/sdk/settings/model.py index 3c238e7195..53e125754a 100644 --- a/openhands-sdk/openhands/sdk/settings/model.py +++ b/openhands-sdk/openhands/sdk/settings/model.py @@ -361,7 +361,7 @@ def _migrate_agent_settings_v1_to_v2(payload: dict[str, Any]) -> dict[str, Any]: persisted payloads carried ``agent_kind: 'llm'``. The two classes are field-compatible (``LLMAgentSettings`` is a subclass of ``OpenHandsAgentSettings`` that only narrows the discriminator literal), - and ``LLMAgentSettings`` is scheduled for removal in v1.23.0. Rewriting + and ``LLMAgentSettings`` is scheduled for removal in v1.24.0. Rewriting the discriminator on read lets callers that explicitly validate as ``OpenHandsAgentSettings`` (the canonical class) accept legacy data without losing any fields. @@ -1132,7 +1132,7 @@ class LLMAgentSettings(OpenHandsAgentSettings): Use :class:`OpenHandsAgentSettings` for all new code. - Scheduled for removal in v1.23.0. + Scheduled for removal in v1.24.0. """ # Keep agent_kind as Literal["llm"] so the API-breakage checker sees no @@ -1228,7 +1228,7 @@ class AgentSettings(LLMAgentSettings): * Use :func:`validate_agent_settings` to validate raw payloads into the correct variant. - Scheduled for removal in v1.23.0. + Scheduled for removal in v1.24.0. """ @classmethod @@ -1250,7 +1250,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: warn_deprecated( "AgentSettings", deprecated_in="1.17.0", - removed_in="1.23.0", + removed_in="1.24.0", details=( "Use ``OpenHandsAgentSettings`` (for an LLM agent) or " "``ACPAgentSettings`` (for an ACP agent) directly; use " From b3870047577adf7fe18d2a50fa605a5a41cfb0c7 Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 11 May 2026 16:30:27 +0000 Subject: [PATCH 4/4] Restore AgentSettings removal deadline Co-authored-by: openhands --- openhands-sdk/openhands/sdk/settings/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openhands-sdk/openhands/sdk/settings/model.py b/openhands-sdk/openhands/sdk/settings/model.py index 53e125754a..18f8d97d89 100644 --- a/openhands-sdk/openhands/sdk/settings/model.py +++ b/openhands-sdk/openhands/sdk/settings/model.py @@ -1228,7 +1228,7 @@ class AgentSettings(LLMAgentSettings): * Use :func:`validate_agent_settings` to validate raw payloads into the correct variant. - Scheduled for removal in v1.24.0. + Scheduled for removal in v1.23.0. """ @classmethod @@ -1250,7 +1250,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: warn_deprecated( "AgentSettings", deprecated_in="1.17.0", - removed_in="1.24.0", + removed_in="1.23.0", details=( "Use ``OpenHandsAgentSettings`` (for an LLM agent) or " "``ACPAgentSettings`` (for an ACP agent) directly; use "