OpenHands update-sdk
This skill should be used when the user asks to "update SDK", "bump SDK version", "pin SDK to a commit", "test unreleased SDK", "update agent-server image", "bump the version", "prepare a release", "what files change for a release", or needs to know how SDK packages are managed in the OpenHands repository. For detailed reference material, see references/docker-image-locations.md and references/sdk-pinning-examples.md in this skill directory.
git clone https://github.com/OpenHands/OpenHands
T=$(mktemp -d) && git clone --depth=1 https://github.com/OpenHands/OpenHands "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.agents/skills/update-sdk" ~/.claude/skills/all-hands-ai-openhands-update-sdk && rm -rf "$T"
.agents/skills/update-sdk/SKILL.mdUpdate SDK
Bump SDK packages (
openhands-sdk, openhands-agent-server, openhands-tools), pin them to unreleased commits for testing, and cut an OpenHands release.
Quick Summary — How Many Files Change?
| Activity | Manual edits | Auto-regenerated | Total |
|---|---|---|---|
| SDK bump (released PyPI version) | 2 | 3 | 5 |
| SDK pin (unreleased git commit) | 3 | 3 | 6 |
| Release commit (version bump) | 3 | 0 | 3 |
The 3 auto-regenerated files are always:
poetry.lock, uv.lock, enterprise/poetry.lock.
SDK Package Bump — 2 Files + 3 Lock Files
Land as a separate PR before the release. Examples:
929dcc3 (SDK 1.11.5), cd235cc (SDK 1.11.4).
| File | What to change |
|---|---|
| , , in two sections: the array (PEP 508) and |
| constant — set to |
Then regenerate lock files:
poetry lock && uv lock && cd enterprise && poetry lock && cd ..
Docker Image Locations — All Hardcoded References
For the complete inventory of every file containing a hardcoded Docker image tag or repository, see
references/docker-image-locations.md. Key files that must stay in sync during an SDK bump:
| File | Image reference | Updated during SDK bump? |
|---|---|---|
| | ✅ Yes |
| default | ✅ Should be |
| + defaults | ✅ Should be |
CI enforcement:
validates version consistency and compose file image references on every PR and push to main..github/workflows/check-version-consistency.yml
⚠️ Docker Image Tag Gotcha (merge-commit SHA)
The SDK CI in
software-agent-sdk repo tags Docker images with the GitHub Actions merge-commit SHA, NOT the PR head-commit SHA. When pinning to an SDK PR branch:
- Check the SDK PR description for the actual image tag (look for the
section)AGENT_SERVER_IMAGES - Or query the CI logs: the "Consolidate Build Information" job prints
"short_sha": "<tag>" - The merge-commit SHA differs from the head SHA shown in the PR
For released SDK versions, images use a version tag (e.g.,
1.12.0-python) — no merge-commit ambiguity.
Cutting a Release — 3 Files
A release commit updates the version string across 3 files. Gold-standard examples: 1.3.0 (
d063c8c), 1.4.0 (495f48b).
| File | What to change |
|---|---|
| under |
| |
| in two places (root object and ) |
Note:
reads the version fromopenhands/version.pyat runtime — no manual edit needed there.pyproject.toml
Compose Files (2 files)
Both compose files should use
ghcr.io/openhands/agent-server with the current SDK version tag.
| File | What to verify |
|---|---|
| defaults to agent-server, is current |
| Same — must use agent-server, not runtime |
Release Workflow
Step 1: Verify the SDK bump has landed
grep -n "openhands-sdk\|openhands-agent-server\|openhands-tools" pyproject.toml grep -n "AGENT_SERVER_IMAGE" openhands/app_server/sandbox/sandbox_spec_service.py grep "AGENT_SERVER_IMAGE_TAG" docker-compose.yml containers/dev/compose.yml
Step 2: Bump version numbers
# Edit pyproject.toml, frontend/package.json, frontend/package-lock.json git add pyproject.toml frontend/package.json frontend/package-lock.json git commit -m "Release X.Y.Z" git tag X.Y.Z
Create a
saas-rel-X.Y.Z branch from the tagged commit for the SaaS deployment pipeline.
Step 3: Images get tagged automatically
Every push to
main / saas-rel-* / oss-rel-* builds and publishes ghcr.io/openhands/openhands and ghcr.io/openhands/enterprise-server images for that commit (tagged by SHA, short SHA, and branch name).
Pushing a git tag
X.Y.Z then tags the images for that commit with X.Y.Z, X.Y, X, and latest. Non-semver tags just get their literal name applied.
Requires the commit to already be built. If you push the tag too early, the retag CI job fails loudly — re-run it from the Actions UI once the build completes.
Development: Pin SDK to an Unreleased Commit
For detailed examples of all pinning formats (commit, branch, uv-only), see
references/sdk-pinning-examples.md.
Files to change (3 manual + 3 lock files)
| File | What to change |
|---|---|
| Pin all 3 SDK packages in both and |
| — use the merge-commit SHA tag, NOT the head-commit SHA |
| default (for local development) |
| Auto-regenerated via |
| Auto-regenerated via |
| Auto-regenerated via |
CI guard
The
check-package-versions.yml workflow blocks merging to main if [tool.poetry.dependencies] contains any rev fields. This ensures unreleased SDK pins do not accidentally ship in a release.