feat: add local_agent_server_mode to OpenHandsCloudWorkspace#2490
feat: add local_agent_server_mode to OpenHandsCloudWorkspace#2490
Conversation
Python API breakage checks — ✅ PASSEDResult: ✅ PASSED |
REST API breakage checks (OpenAPI) — ✅ PASSEDResult: ✅ PASSED |
|
Added a commit to expose the SDK packages (openhands-sdk, openhands-workspace, openhands-tools) to the system Python in the This is needed for the automations service dispatcher (deploy PR #3456) — when an automation script runs inside a sandbox with Once this PR's image finishes building, we'll reference the new image tag in the deploy PR to enable full SDK automation scripts. |
When saas_runtime_mode=True, the workspace assumes it is already running inside an OpenHands Cloud Runtime sandbox and connects directly to the local agent-server at localhost instead of provisioning a sandbox via the Cloud API. This supports the automation service architecture (ADR-0002) where SDK scripts execute inside pre-existing Cloud Runtimes. The workspace skips sandbox creation/deletion and points the HTTP client at localhost:<port>. Changes: - Add saas_runtime_mode and agent_server_port fields - Make cloud_api_url/cloud_api_key optional (with validator enforcing them when saas_runtime_mode=False) - Add _init_saas_runtime_mode() for the local init path - Skip sandbox cleanup in saas_runtime_mode - Add 8 new tests covering the feature Co-authored-by: openhands <openhands@all-hands.dev>
cloud_api_url and cloud_api_key are needed even in saas_runtime_mode for get_llms() and get_secrets() calls to the Cloud API. Reverts them to required str fields and removes the conditional model_validator. Co-authored-by: openhands <openhands@all-hands.dev>
Add PYTHONPATH pointing to the agent-server venv's site-packages in both source and source-minimal Docker targets. This allows user scripts running inside the sandbox (e.g. automation entrypoints using OpenHandsCloudWorkspace with saas_runtime_mode=True) to import openhands-sdk, openhands-workspace, and openhands-tools directly without needing to activate the venv. Co-authored-by: openhands <openhands@all-hands.dev>
This reverts commit 73c517c.
When automation_callback_url is set, __exit__ POSTs completion status (COMPLETED or FAILED with error detail) to the automation service before cleanup(). Best-effort — callback failures are logged, not raised. New fields: - automation_callback_url: URL to POST to on exit - automation_run_id: included in callback payload Co-authored-by: openhands <openhands@all-hands.dev>
62d08aa to
b3841c4
Compare
Default to AGENT_SERVER_PORT environment variable for the agent server port, falling back to 60000 if not set. Explicit kwarg still takes precedence over the env var. Co-authored-by: openhands <openhands@all-hands.dev>
This reverts commit 975647d.
_init_saas_runtime_mode() now reads sandbox identity from env vars so get_llm() and get_secrets() work when the SDK runs inside a Cloud sandbox (automation dispatch path). Env vars (injected by the automation dispatcher): SANDBOX_ID — sandbox's Cloud API identifier SESSION_API_KEY — session key for sandbox settings auth (falls back to OH_SESSION_API_KEYS_0 set by the runtime) Also reads AGENT_SERVER_PORT env var for the local agent-server port. Constructor param sandbox_id= takes precedence over env var. Also fixes pre-existing test URL mismatch in test_cleanup_deletes_sandbox. Co-authored-by: openhands <openhands@all-hands.dev>
|
I'm on it! xingyaoww can track my progress at all-hands.dev |
Co-authored-by: openhands <openhands@all-hands.dev>
SummaryThe CI failure in PR #2490 (job What was wrongThe What was fixed (commit
|
all-hands-bot
left a comment
There was a problem hiding this comment.
🟢 Good taste - Clean implementation with solid engineering fundamentals.
What's good:
• Simple data structures eliminate complexity
• Low nesting (max 2 levels), proper early returns
• Comprehensive test coverage for new behavior
• Backward compatible (all new fields default to existing behavior)
• Fixes test bug for cleanup endpoint
• Best-effort callback design won't break on errors
Minor note:
The test fix for test_cleanup_deletes_sandbox (line 167) corrects a pre-existing mismatch between test expectation and implementation. Worth mentioning in the PR description as a drive-by fix.
_init_saas_runtime_mode() was setting self._session_api_key but not propagating it to self.api_key (the RemoteWorkspaceMixin field). The shared HTTP client (used by RemoteConversation) builds its headers from self.api_key via _headers, so conversations got no X-Session-API-Key header → 401 from the local agent-server. Mirrors what _start_sandbox() already does at line 268. Co-authored-by: openhands <openhands@all-hands.dev>
a4d2797 to
13a4aa6
Compare
all-hands-bot
left a comment
There was a problem hiding this comment.
🟢 Good taste - Clean implementation that solves a real problem.
What works:
• Simple data structures, no unnecessary complexity
• Low nesting (max 2-3 levels), clean control flow
• Backward compatible - all defaults preserve existing behavior
• Comprehensive test coverage for new paths
• Pragmatic error handling (best-effort callback, defensive cleanup guards)
• Fixes pre-existing test bug for cleanup endpoint URL
KEY INSIGHT: The dual-mode design (normal vs SaaS runtime) is handled elegantly through early branching in model_post_init, eliminating special cases throughout the rest of the code.
Prevent LLM-driven agents from accessing SESSION_API_KEY via terminal commands. This credential grants access to user secrets via the SaaS API and must remain isolated to the SDK's Python process. - Add SESSION_API_KEY to _SENSITIVE_ENV_VARS in sanitized_env() - Add security tests verifying terminal tool cannot access the key Co-authored-by: openhands <openhands@all-hands.dev>
Coverage Report •
|
||||||||||||||||||||||||||||||
Pin SDK packages to commit 8e797ec3 which includes the security fix that strips SESSION_API_KEY from subprocess environments, preventing LLM-driven agents from accessing user secrets via terminal commands. SDK PR: OpenHands/software-agent-sdk#2490 Agent-server image: 9c0a58d-python (merge-commit SHA from CI) Co-authored-by: openhands <openhands@all-hands.dev>
|
@xingyaoww @malhotra5 I was trying to make sure I understood this PR and in the process created some additional documentation at OpenHands/docs#414. Would your feedback. |
Co-authored-by: openhands <openhands@all-hands.dev>
The automation service's completion callback endpoint requires authentication via Bearer token. Without this header, the callback returns 401 Unauthorized. The cloud_api_key is already available on the workspace instance (set from OPENHANDS_API_KEY env var), so we just need to include it in the request. Co-authored-by: openhands <openhands@all-hands.dev>
…utomation fields from env vars - Rename saas_runtime_mode to local_agent_server_mode for clarity - Move automation_callback_url and automation_run_id from constructor fields to private attributes read from env vars (AUTOMATION_CALLBACK_URL, AUTOMATION_RUN_ID) during local_agent_server_mode init - Update all tests accordingly Co-authored-by: openhands <openhands@all-hands.dev>
Summary
Add
local_agent_server_modetoOpenHandsCloudWorkspaceto support the automation service architecture described in RFC #13275.When
local_agent_server_mode=True, the workspace assumes it is already running inside an OpenHands Cloud Runtime sandbox. Instead of creating or managing a sandbox via the Cloud API, it connects directly to the local agent-server athttp://localhost:<port>(default port 60000). Cloud API credentials are still required forget_llms()/get_secrets()calls.New fields:
local_agent_server_mode: bool = False— enables local agent-server modeagent_server_port: int = 60000— configurable local portBehavioral changes:
model_post_initskips sandbox creation/wait in local agent-server modecleanup()only closes the HTTP client in local agent-server mode (no sandbox deletion)automation_callback_urlandautomation_run_idare read from env vars (AUTOMATION_CALLBACK_URL,AUTOMATION_RUN_ID) rather than constructor argsUsage:
Checklist
Agent Server images for this PR
• GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server
Variants & Base Images
eclipse-temurin:17-jdknikolaik/python-nodejs:python3.13-nodejs22golang:1.21-bookwormPull (multi-arch manifest)
# Each variant is a multi-arch manifest supporting both amd64 and arm64 docker pull ghcr.io/openhands/agent-server:33e1694-pythonRun
All tags pushed for this build
About Multi-Architecture Support
33e1694-python) is a multi-arch manifest supporting both amd64 and arm6433e1694-python-amd64) are also available if needed