From 5175125b7e9b8b79ce1394462780e58f95bde1b4 Mon Sep 17 00:00:00 2001 From: simonrosenberg <157206163+simonrosenberg@users.noreply.github.com> Date: Wed, 1 Jul 2026 14:03:28 +0200 Subject: [PATCH] chore: bump SDK and agent-server to 1.30.0 (#15079) Co-authored-by: Simon Rosenberg Co-authored-by: Claude Sonnet 5 --- containers/dev/compose.yml | 2 +- dev_config/python/.pre-commit-config.yaml | 4 +-- docker-compose.yml | 2 +- enterprise/poetry.lock | 27 ++++++++++--------- .../app_conversation_models.py | 18 +------------ .../sandbox/sandbox_spec_service.py | 2 +- openhands/app_server/settings/llm_profiles.py | 27 +++++++++++++++++++ poetry.lock | 21 ++++++++------- pyproject.toml | 12 ++++----- tests/unit/app_server/test_profiles_api.py | 25 +++++++++++++++++ uv.lock | 24 ++++++++--------- 11 files changed, 101 insertions(+), 63 deletions(-) diff --git a/containers/dev/compose.yml b/containers/dev/compose.yml index fb395734b2..14e92e5c21 100644 --- a/containers/dev/compose.yml +++ b/containers/dev/compose.yml @@ -13,7 +13,7 @@ services: - DOCKER_HOST_ADDR=host.docker.internal # - AGENT_SERVER_IMAGE_REPOSITORY=${AGENT_SERVER_IMAGE_REPOSITORY:-ghcr.io/openhands/agent-server} - - AGENT_SERVER_IMAGE_TAG=${AGENT_SERVER_IMAGE_TAG:-1.29.0-python} + - AGENT_SERVER_IMAGE_TAG=${AGENT_SERVER_IMAGE_TAG:-1.30.0-python} - SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} - WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace} ports: diff --git a/dev_config/python/.pre-commit-config.yaml b/dev_config/python/.pre-commit-config.yaml index 062aa7f185..17f5682ae7 100644 --- a/dev_config/python/.pre-commit-config.yaml +++ b/dev_config/python/.pre-commit-config.yaml @@ -57,8 +57,8 @@ repos: types-Markdown, pydantic, lxml, - "openhands-sdk==1.28.0", - "openhands-tools==1.28.0", + "openhands-sdk==1.30.0", + "openhands-tools==1.30.0", "sqlalchemy>=2.0", ] # To see gaps add `--html-report mypy-report/` diff --git a/docker-compose.yml b/docker-compose.yml index 655501f59f..1e6344c9ea 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: container_name: openhands-app-${DATE:-} environment: - AGENT_SERVER_IMAGE_REPOSITORY=${AGENT_SERVER_IMAGE_REPOSITORY:-ghcr.io/openhands/agent-server} - - AGENT_SERVER_IMAGE_TAG=${AGENT_SERVER_IMAGE_TAG:-1.29.0-python} + - AGENT_SERVER_IMAGE_TAG=${AGENT_SERVER_IMAGE_TAG:-1.30.0-python} #- SANDBOX_USER_ID=${SANDBOX_USER_ID:-1234} # enable this only if you want a specific non-root sandbox user but you will have to manually adjust permissions of ~/.openhands for this user - WORKSPACE_MOUNT_PATH=${WORKSPACE_BASE:-$PWD/workspace} ports: diff --git a/enterprise/poetry.lock b/enterprise/poetry.lock index 26eb0bf588..988ce7402c 100644 --- a/enterprise/poetry.lock +++ b/enterprise/poetry.lock @@ -5776,14 +5776,14 @@ numpy = {version = ">=2", markers = "python_version >= \"3.9\""} [[package]] name = "openhands-agent-server" -version = "1.29.0" +version = "1.30.0" description = "OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent" optional = false python-versions = ">=3.12" groups = ["main"] files = [ - {file = "openhands_agent_server-1.29.0-py3-none-any.whl", hash = "sha256:39c633d6be822c264076ff1851317b192cc796ddf48c9146497f78c554c99127"}, - {file = "openhands_agent_server-1.29.0.tar.gz", hash = "sha256:f9e16a9d67b6bee9f8eefc4df46aedd0a4a3e03a8bcd150d679b7775076b7bf2"}, + {file = "openhands_agent_server-1.30.0-py3-none-any.whl", hash = "sha256:759e337b2b029d1891e8eca351e2af5f05b304f5a800a346b216f56e5964ab89"}, + {file = "openhands_agent_server-1.30.0.tar.gz", hash = "sha256:af2290355350fa5c51c1ec5f2faea373124b08af2c12f25ef902567789c8371a"}, ] [package.dependencies] @@ -5814,7 +5814,7 @@ aiohttp = ">=3.14.1" anthropic = {version = "*", extras = ["vertex"]} anyio = "4.9" asyncpg = ">=0.30" -authlib = ">=1.6.12" +authlib = ">=1.6.12,<1.7.0 || >1.7.0" bashlex = ">=0.18" binaryornot = ">=0.5.0,<1" boto3 = "*" @@ -5847,9 +5847,9 @@ mcp = ">=1.25" memory-profiler = ">=0.61" numpy = "*" openai = "2.33.0" -openhands-agent-server = "1.29.0" -openhands-sdk = "1.29.0" -openhands-tools = "1.29.0" +openhands-agent-server = "1.30.0" +openhands-sdk = "1.30.0" +openhands-tools = "1.30.0" opentelemetry-api = ">=1.33.1" opentelemetry-exporter-otlp-proto-grpc = ">=1.33.1" orjson = ">=3.11.6" @@ -5902,14 +5902,14 @@ url = ".." [[package]] name = "openhands-sdk" -version = "1.29.0" +version = "1.30.0" description = "OpenHands SDK - Core functionality for building AI agents" optional = false python-versions = ">=3.12" groups = ["main"] files = [ - {file = "openhands_sdk-1.29.0-py3-none-any.whl", hash = "sha256:ae9f4ec0b1fd86e3fd4c74682a9ee477614e790457ed06c675ad6caf9eef5194"}, - {file = "openhands_sdk-1.29.0.tar.gz", hash = "sha256:2b38dec04858535ff78fdc961ffc44cee338aca6947c0ddb70c6926e5ac883af"}, + {file = "openhands_sdk-1.30.0-py3-none-any.whl", hash = "sha256:53423bd4916c578ccb8f8f488dfcc16669343f2a3ac0c3db955793e704f734a2"}, + {file = "openhands_sdk-1.30.0.tar.gz", hash = "sha256:b48a75adb8d2c48d6a53d0022e3521d63caeb5931b99d5a56906a24a4eec3e72"}, ] [package.dependencies] @@ -5933,17 +5933,18 @@ websockets = ">=12" [package.extras] boto3 = ["boto3 (>=1.35.0)"] +vertex = ["google-cloud-aiplatform (>=1.38)"] [[package]] name = "openhands-tools" -version = "1.29.0" +version = "1.30.0" description = "OpenHands Tools - Runtime tools for AI agents" optional = false python-versions = ">=3.12" groups = ["main"] files = [ - {file = "openhands_tools-1.29.0-py3-none-any.whl", hash = "sha256:bb50b62a1929958e4fd8991fd8dbd6b8269c87da74286a925c066d0ac3dd915f"}, - {file = "openhands_tools-1.29.0.tar.gz", hash = "sha256:3427966f5cb21cc64932dde73173772ccf384ba25f4f599501d540b3f2dd7d01"}, + {file = "openhands_tools-1.30.0-py3-none-any.whl", hash = "sha256:349af6c15b7d9808edc529d52e0e06efea064187662bef0d2e6c3a0d219f6cfa"}, + {file = "openhands_tools-1.30.0.tar.gz", hash = "sha256:8762808cb481a7d5805c0f66ac62228dc25ce2baf336ab2d446ca772c761c864"}, ] [package.dependencies] diff --git a/openhands/app_server/app_conversation/app_conversation_models.py b/openhands/app_server/app_conversation/app_conversation_models.py index fc4c928399..ac668ec450 100644 --- a/openhands/app_server/app_conversation/app_conversation_models.py +++ b/openhands/app_server/app_conversation/app_conversation_models.py @@ -1,10 +1,9 @@ -import re from datetime import datetime from enum import Enum from typing import Any, Literal from uuid import UUID, uuid4 -from pydantic import BaseModel, Field, SecretStr, computed_field, field_serializer +from pydantic import BaseModel, Field, SecretStr, computed_field from openhands.agent_server.models import ( ImageContent, @@ -54,16 +53,6 @@ class ConversationTrigger(Enum): AUTOMATION = 'automation' -def _redact_url_credentials(url: str) -> str: - """Redact embedded credentials from a URL (https://user:token@host → https://****@host). - - # TODO: replace with `from openhands.sdk.utils.redact import redact_url_credentials` - # once the SDK pin is bumped to include OpenHands/software-agent-sdk#2154. - """ - m = re.match(r'^(https?://)([^@/]+)@(.+)$', url) - return f'{m.group(1)}****@{m.group(3)}' if m else url - - class AgentType(Enum): """Agent type for conversation.""" @@ -83,11 +72,6 @@ class PluginSpec(PluginSource): description='User-provided values for plugin input parameters', ) - @field_serializer('source') - @classmethod - def _serialize_source(cls, source: str) -> str: - return _redact_url_credentials(source) - @property def display_name(self) -> str: """Extract a friendly display name from the plugin source. diff --git a/openhands/app_server/sandbox/sandbox_spec_service.py b/openhands/app_server/sandbox/sandbox_spec_service.py index 51a2bdbf6b..5d4d3277de 100644 --- a/openhands/app_server/sandbox/sandbox_spec_service.py +++ b/openhands/app_server/sandbox/sandbox_spec_service.py @@ -14,7 +14,7 @@ from openhands.sdk.utils.models import DiscriminatedUnionMixin # The version of the agent server to use for deployments. # Typically this will be the same as the values from the pyproject.toml -AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:1.29.0-python' +AGENT_SERVER_IMAGE = 'ghcr.io/openhands/agent-server:1.30.0-python' class SandboxSpecService(ABC): diff --git a/openhands/app_server/settings/llm_profiles.py b/openhands/app_server/settings/llm_profiles.py index 4fedeac9b4..01bb9c7e24 100644 --- a/openhands/app_server/settings/llm_profiles.py +++ b/openhands/app_server/settings/llm_profiles.py @@ -106,6 +106,33 @@ class StrictLLM(LLM): model_config = ConfigDict(extra='forbid') + @model_validator(mode='wrap') + @classmethod + def _restore_is_subscription(cls, data: Any, handler: Any) -> Any: + """Temporary workaround for a non-composable SDK validator. + + ``LLM`` defines a validator of this same name (``mode="wrap"``) + that restores ``_is_subscription`` from this computed field so it + survives a dump/validate round trip. It does so by reading its own + raw ``data`` argument directly, independent of any subclass + ``mode="before"`` validator's transformation of the input — so a + normal ``before`` validator can't neutralize it, and the *only* + way to override the behavior is to shadow this exact method name. + See OpenHands/software-agent-sdk#3942. + + Strip ``is_subscription`` from the input before validation (so + ``extra='forbid'`` doesn't reject the GET-response echo as an + unrecognized field on this endpoint's GET-edit-POST round trip) + and don't restore it: it's semantically only ever supposed to be + set via ``LLM.subscription_login()``, never via user-supplied + JSON here. + + TODO: remove once software-agent-sdk#3942 is fixed upstream. + """ + if isinstance(data, dict): + data = {k: v for k, v in data.items() if k != 'is_subscription'} + return handler(data) + class LLMProfiles(BaseModel): """Container for saved LLM configurations. diff --git a/poetry.lock b/poetry.lock index fb4f8df526..ac786853c7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -5520,14 +5520,14 @@ pydantic = ">=1.8" [[package]] name = "openhands-agent-server" -version = "1.29.0" +version = "1.30.0" description = "OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent" optional = false python-versions = ">=3.12" groups = ["main"] files = [ - {file = "openhands_agent_server-1.29.0-py3-none-any.whl", hash = "sha256:39c633d6be822c264076ff1851317b192cc796ddf48c9146497f78c554c99127"}, - {file = "openhands_agent_server-1.29.0.tar.gz", hash = "sha256:f9e16a9d67b6bee9f8eefc4df46aedd0a4a3e03a8bcd150d679b7775076b7bf2"}, + {file = "openhands_agent_server-1.30.0-py3-none-any.whl", hash = "sha256:759e337b2b029d1891e8eca351e2af5f05b304f5a800a346b216f56e5964ab89"}, + {file = "openhands_agent_server-1.30.0.tar.gz", hash = "sha256:af2290355350fa5c51c1ec5f2faea373124b08af2c12f25ef902567789c8371a"}, ] [package.dependencies] @@ -5545,14 +5545,14 @@ wsproto = ">=1.2.0" [[package]] name = "openhands-sdk" -version = "1.29.0" +version = "1.30.0" description = "OpenHands SDK - Core functionality for building AI agents" optional = false python-versions = ">=3.12" groups = ["main"] files = [ - {file = "openhands_sdk-1.29.0-py3-none-any.whl", hash = "sha256:ae9f4ec0b1fd86e3fd4c74682a9ee477614e790457ed06c675ad6caf9eef5194"}, - {file = "openhands_sdk-1.29.0.tar.gz", hash = "sha256:2b38dec04858535ff78fdc961ffc44cee338aca6947c0ddb70c6926e5ac883af"}, + {file = "openhands_sdk-1.30.0-py3-none-any.whl", hash = "sha256:53423bd4916c578ccb8f8f488dfcc16669343f2a3ac0c3db955793e704f734a2"}, + {file = "openhands_sdk-1.30.0.tar.gz", hash = "sha256:b48a75adb8d2c48d6a53d0022e3521d63caeb5931b99d5a56906a24a4eec3e72"}, ] [package.dependencies] @@ -5576,17 +5576,18 @@ websockets = ">=12" [package.extras] boto3 = ["boto3 (>=1.35.0)"] +vertex = ["google-cloud-aiplatform (>=1.38)"] [[package]] name = "openhands-tools" -version = "1.29.0" +version = "1.30.0" description = "OpenHands Tools - Runtime tools for AI agents" optional = false python-versions = ">=3.12" groups = ["main"] files = [ - {file = "openhands_tools-1.29.0-py3-none-any.whl", hash = "sha256:bb50b62a1929958e4fd8991fd8dbd6b8269c87da74286a925c066d0ac3dd915f"}, - {file = "openhands_tools-1.29.0.tar.gz", hash = "sha256:3427966f5cb21cc64932dde73173772ccf384ba25f4f599501d540b3f2dd7d01"}, + {file = "openhands_tools-1.30.0-py3-none-any.whl", hash = "sha256:349af6c15b7d9808edc529d52e0e06efea064187662bef0d2e6c3a0d219f6cfa"}, + {file = "openhands_tools-1.30.0.tar.gz", hash = "sha256:8762808cb481a7d5805c0f66ac62228dc25ce2baf336ab2d446ca772c761c864"}, ] [package.dependencies] @@ -13762,4 +13763,4 @@ cffi = ["cffi (>=1.17,<2.0) ; platform_python_implementation != \"PyPy\" and pyt [metadata] lock-version = "2.1" python-versions = "^3.12,<3.14" -content-hash = "d3e4046361c4dfef83ab27d9492ae39f4daea1392335da0e50d36be01a2808b3" +content-hash = "a35dca483fc1a16e7bfa8c3d48be22cb18a1848fa97bdf9da851b9822800a192" diff --git a/pyproject.toml b/pyproject.toml index 96fa319309..d6dcff959f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,9 +58,9 @@ dependencies = [ "memory-profiler>=0.61", "numpy", "openai==2.33.0", - "openhands-agent-server==1.29.0", - "openhands-sdk==1.29.0", - "openhands-tools==1.29.0", + "openhands-agent-server==1.30.0", + "openhands-sdk==1.30.0", + "openhands-tools==1.30.0", "opentelemetry-api>=1.33.1", "opentelemetry-exporter-otlp-proto-grpc>=1.33.1", "orjson>=3.11.6", @@ -248,9 +248,9 @@ e2b-code-interpreter = { version = "^2.0.0", optional = true } pybase62 = "^1.0.0" # V1 dependencies -openhands-sdk = "==1.29.0" -openhands-agent-server = "==1.29.0" -openhands-tools = "==1.29.0" +openhands-sdk = "==1.30.0" +openhands-agent-server = "==1.30.0" +openhands-tools = "==1.30.0" joserfc = ">=1.0.0" sqlalchemy = { extras = [ "asyncio" ], version = "^2.0.40" } pg8000 = "^1.31.5" diff --git a/tests/unit/app_server/test_profiles_api.py b/tests/unit/app_server/test_profiles_api.py index d4c9630037..1a4d4a0ab4 100644 --- a/tests/unit/app_server/test_profiles_api.py +++ b/tests/unit/app_server/test_profiles_api.py @@ -395,6 +395,31 @@ async def test_edit_profile_round_trip_preserves_api_key(test_client, settings_s assert preserved.api_key.get_secret_value() == 'REAL-KEY-42' +@pytest.mark.asyncio +async def test_save_profile_rejects_client_declared_is_subscription( + test_client, settings_store +): + """A client can't put a profile into subscription mode via this endpoint. + + ``is_subscription`` is a read-only SDK computed field: it's serialized + into GET responses so GET-edit-POST round trips don't 422, but a client + setting it to ``True`` directly must not survive validation, since it + only becomes true via ``LLM.subscription_login()`` — a path this + endpoint doesn't use. This is a temporary compensating control pending + OpenHands/software-agent-sdk#3942; see ``StrictLLM._restore_is_subscription``. + """ + await _seed(settings_store, _base_settings()) + + resp = test_client.post( + '/api/v1/settings/profiles/p', + json={'llm': {'model': 'openai/gpt-4o', 'is_subscription': True}}, + ) + assert resp.status_code == 201 + + stored = await settings_store.load() + assert stored.llm_profiles.get('p').is_subscription is False + + @pytest.mark.asyncio async def test_edit_profile_with_new_api_key_replaces_old(test_client, settings_store): """Counter-test to the round-trip guard: when the user actually types diff --git a/uv.lock b/uv.lock index f8d30c36cd..738094fa94 100644 --- a/uv.lock +++ b/uv.lock @@ -3178,7 +3178,7 @@ wheels = [ [[package]] name = "openhands-agent-server" -version = "1.29.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiosqlite" }, @@ -3193,9 +3193,9 @@ dependencies = [ { name = "websockets" }, { name = "wsproto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b3/91/64405ab306622d62b797595ea20a49e836c040ab04cfcca5e62e3a73427b/openhands_agent_server-1.29.0.tar.gz", hash = "sha256:f9e16a9d67b6bee9f8eefc4df46aedd0a4a3e03a8bcd150d679b7775076b7bf2", size = 167536, upload-time = "2026-06-18T16:11:00.897Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/15/6d872038eb79fda789db1914b77107729952c058b26550fd9080d113e5e7/openhands_agent_server-1.30.0.tar.gz", hash = "sha256:af2290355350fa5c51c1ec5f2faea373124b08af2c12f25ef902567789c8371a", size = 186446, upload-time = "2026-07-01T10:29:56.859Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/86/5f7fcb4a62e57c4fa363d5e11fdcfb49c51051117a800413cc61ce41bd25/openhands_agent_server-1.29.0-py3-none-any.whl", hash = "sha256:39c633d6be822c264076ff1851317b192cc796ddf48c9146497f78c554c99127", size = 193097, upload-time = "2026-06-18T16:10:53.41Z" }, + { url = "https://files.pythonhosted.org/packages/bb/8c/64dba47bc88e0989bdcd9d5879bf04435bee9f797bdb64a50afb390e9e49/openhands_agent_server-1.30.0-py3-none-any.whl", hash = "sha256:759e337b2b029d1891e8eca351e2af5f05b304f5a800a346b216f56e5964ab89", size = 213756, upload-time = "2026-07-01T10:29:51.576Z" }, ] [[package]] @@ -3356,9 +3356,9 @@ requires-dist = [ { name = "memory-profiler", specifier = ">=0.61" }, { name = "numpy" }, { name = "openai", specifier = "==2.33.0" }, - { name = "openhands-agent-server", specifier = "==1.29.0" }, - { name = "openhands-sdk", specifier = "==1.29.0" }, - { name = "openhands-tools", specifier = "==1.29.0" }, + { name = "openhands-agent-server", specifier = "==1.30.0" }, + { name = "openhands-sdk", specifier = "==1.30.0" }, + { name = "openhands-tools", specifier = "==1.30.0" }, { name = "opentelemetry-api", specifier = ">=1.33.1" }, { name = "opentelemetry-exporter-otlp-proto-grpc", specifier = ">=1.33.1" }, { name = "orjson", specifier = ">=3.11.6" }, @@ -3436,7 +3436,7 @@ test = [ [[package]] name = "openhands-sdk" -version = "1.29.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-client-protocol" }, @@ -3457,14 +3457,14 @@ dependencies = [ { name = "tree-sitter-bash" }, { name = "websockets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/13/d7/03c75453aa50e016e10989bb11e6fe5015db659c3d6d541a38aa60ec7920/openhands_sdk-1.29.0.tar.gz", hash = "sha256:2b38dec04858535ff78fdc961ffc44cee338aca6947c0ddb70c6926e5ac883af", size = 588347, upload-time = "2026-06-18T16:10:58.279Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/32/a8fc01eefe1a9857ef9432c833841beb216ddd85ee8d6c2968013be267c1/openhands_sdk-1.30.0.tar.gz", hash = "sha256:b48a75adb8d2c48d6a53d0022e3521d63caeb5931b99d5a56906a24a4eec3e72", size = 588645, upload-time = "2026-07-01T10:29:58.062Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/7d/faacecdc314f1deb85f7f123147c4522a77200c8b744496269b830dded36/openhands_sdk-1.29.0-py3-none-any.whl", hash = "sha256:ae9f4ec0b1fd86e3fd4c74682a9ee477614e790457ed06c675ad6caf9eef5194", size = 710013, upload-time = "2026-06-18T16:10:59.683Z" }, + { url = "https://files.pythonhosted.org/packages/32/e9/330fa562f63732c545d7fa0c8b68b8575cd39c952c605150bf41134932f3/openhands_sdk-1.30.0-py3-none-any.whl", hash = "sha256:53423bd4916c578ccb8f8f488dfcc16669343f2a3ac0c3db955793e704f734a2", size = 708572, upload-time = "2026-07-01T10:29:53.107Z" }, ] [[package]] name = "openhands-tools" -version = "1.29.0" +version = "1.30.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "binaryornot" }, @@ -3478,9 +3478,9 @@ dependencies = [ { name = "tree-sitter" }, { name = "tree-sitter-bash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/31/95c9575bca5124dec2e603b32ebf15363b6a3e70f7d8becfa1e025c1df5e/openhands_tools-1.29.0.tar.gz", hash = "sha256:3427966f5cb21cc64932dde73173772ccf384ba25f4f599501d540b3f2dd7d01", size = 144895, upload-time = "2026-06-18T16:10:54.664Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/f5/017a969303eeb3c614d924e744b07e75540dc93e6786c8e1486654e0b74d/openhands_tools-1.30.0.tar.gz", hash = "sha256:8762808cb481a7d5805c0f66ac62228dc25ce2baf336ab2d446ca772c761c864", size = 145603, upload-time = "2026-07-01T10:29:59.418Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/49/0375502eb628d5df7e4dab08bfedcdbd5caec8c81d6dc2176ead1c7f9423/openhands_tools-1.29.0-py3-none-any.whl", hash = "sha256:bb50b62a1929958e4fd8991fd8dbd6b8269c87da74286a925c066d0ac3dd915f", size = 190143, upload-time = "2026-06-18T16:10:55.842Z" }, + { url = "https://files.pythonhosted.org/packages/51/3d/c3a754c1f010f3d4b100357b83b306c9efdcaca69742cc45ad1d2b386305/openhands_tools-1.30.0-py3-none-any.whl", hash = "sha256:349af6c15b7d9808edc529d52e0e06efea064187662bef0d2e6c3a0d219f6cfa", size = 190947, upload-time = "2026-07-01T10:29:54.436Z" }, ] [[package]]