Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
"sdk/guides/agent-server/api-sandbox",
"sdk/guides/agent-server/cloud-workspace",
"sdk/guides/agent-server/custom-tools",
"sdk/guides/agent-server/settings-secrets-api",
{
"group": "API Reference",
"openapi": {
Expand Down
29 changes: 24 additions & 5 deletions sdk/guides/agent-acp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

> A ready-to-run example is available [here](#ready-to-run-example)!

`ACPAgent` lets you use any [Agent Client Protocol](https://agentclientprotocol.com/protocol/overview) server as the backend for an OpenHands conversation. Instead of calling an LLM directly, the agent spawns an ACP server subprocess and communicates with it over JSON-RPC. The server manages its own LLM, tools, and execution — your code just sends messages and collects responses.

Check warning on line 8 in sdk/guides/agent-acp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-acp.mdx#L8

Did you really mean 'subprocess'?

## Basic Usage

Expand Down Expand Up @@ -58,7 +58,7 @@
The prompt assembly works as follows:

1. The conversation layer builds the user `MessageEvent`, including any per-turn `extended_content` (e.g. triggered-skill injections).
2. `ACPAgent._build_acp_prompt()` collects all text blocks from the message and appends the rendered `AgentContext` prompt (datetime, repo context, available skills, system suffix) via `to_acp_prompt_context()`.

Check warning on line 61 in sdk/guides/agent-acp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-acp.mdx#L61

Did you really mean 'datetime'?
3. The combined text is sent as a single user message to the ACP server.

<Note>
Expand All @@ -78,11 +78,11 @@
| `load_user_skills` | ✅ | Load skills from `~/.openhands/skills/` |
| `load_public_skills` | ✅ | Load skills from the public extensions repo |
| `marketplace_path` | ✅ | Filter public skills via marketplace JSON |
| `secrets` | ❌ | ACP subprocesses do not use OpenHands secret injection |

Check warning on line 81 in sdk/guides/agent-acp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-acp.mdx#L81

Did you really mean 'subprocesses'?

Passing `secrets` (or any future field marked `acp_compatible: False`) raises `NotImplementedError`.

### What ACPAgent Does Not Support

Check warning on line 85 in sdk/guides/agent-acp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-acp.mdx#L85

Did you really mean 'ACPAgent'?

Because the ACP server manages its own tools, context window, and execution, these `AgentBase` features are not available on `ACPAgent`:

Expand All @@ -93,7 +93,7 @@

Passing any of these raises `NotImplementedError` at initialization.

## ACPAgent with RemoteConversation

Check warning on line 96 in sdk/guides/agent-acp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-acp.mdx#L96

Did you really mean 'ACPAgent'?

`ACPAgent` also works with remote agent-server deployments such as `APIRemoteWorkspace`, `DockerWorkspace`, and other `RemoteWorkspace`-backed setups.

Expand All @@ -111,7 +111,7 @@

## How It Works

- **Subprocess delegation**: `ACPAgent` spawns the ACP server and communicates via JSON-RPC over stdin/stdout

Check warning on line 114 in sdk/guides/agent-acp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-acp.mdx#L114

Did you really mean 'Subprocess'?
- **Server-managed execution**: The ACP server handles its own LLM calls, tools, and context — your code just sends messages
- **Auto-approval**: Permission requests from the server are automatically granted, so ensure you trust the ACP server you're running
- **Metrics collection**: Token usage and costs from the server are captured into the agent's `LLM.metrics`
Expand Down Expand Up @@ -161,7 +161,7 @@

## Cleanup

Always call `agent.close()` when you are done to terminate the ACP server subprocess. A `try/finally` block is recommended:

Check warning on line 164 in sdk/guides/agent-acp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-acp.mdx#L164

Did you really mean 'subprocess'?

```python icon="python"
agent = ACPAgent(acp_command=["npx", "-y", "@agentclientprotocol/claude-agent-acp"])
Expand All @@ -185,7 +185,8 @@
This example shows how to use an ACP-compatible server (claude-agent-acp)
as the agent backend instead of direct LLM calls. It also demonstrates
``ask_agent()`` — a stateless side-question that forks the ACP session
and leaves the main conversation untouched.
and leaves the main conversation untouched — and sending an image alongside
text to verify multimodal (vision) input support.

Prerequisites:
- Node.js / npx available
Expand All @@ -197,23 +198,41 @@

import os

from openhands.sdk import ImageContent, Message, TextContent
from openhands.sdk.agent import ACPAgent
from openhands.sdk.conversation import Conversation


IMAGE_URL = "https://github.com/OpenHands/docs/raw/main/openhands/static/img/logo.png"

agent = ACPAgent(acp_command=["npx", "-y", "@agentclientprotocol/claude-agent-acp"])

try:
cwd = os.getcwd()
conversation = Conversation(agent=agent, workspace=cwd)

# --- Main conversation turn ---
# --- Main conversation turn (text only) ---
conversation.send_message(
"List the Python source files under openhands-sdk/openhands/sdk/agent/, "
"then read the __init__.py and summarize what agent classes are exported."
)
conversation.run()

# --- Image input turn (text + image) ---
print("\n--- image input ---")
conversation.send_message(
Message(
role="user",
content=[
TextContent(
text="Describe what you see in this image in one sentence."
),
ImageContent(image_urls=[IMAGE_URL]),
],
)
)
conversation.run()

# --- ask_agent: stateless side-question via fork_session ---
print("\n--- ask_agent ---")
response = conversation.ask_agent(
Expand Down Expand Up @@ -248,7 +267,7 @@
This example is available on GitHub: [examples/02_remote_agent_server/09_acp_agent_with_remote_runtime.py](https://github.com/OpenHands/software-agent-sdk/blob/main/examples/02_remote_agent_server/09_acp_agent_with_remote_runtime.py)
</Note>

This example shows how to run an ACPAgent in a remote sandboxed environment via the Runtime API, using `APIRemoteWorkspace`:

Check warning on line 270 in sdk/guides/agent-acp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-acp.mdx#L270

Did you really mean 'ACPAgent'?

Check warning on line 270 in sdk/guides/agent-acp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-acp.mdx#L270

Did you really mean 'sandboxed'?

```python icon="python" expandable examples/02_remote_agent_server/09_acp_agent_with_remote_runtime.py
"""Example: ACPAgent with Remote Runtime via API.
Expand Down Expand Up @@ -294,9 +313,9 @@
runtime_api_key = os.getenv("RUNTIME_API_KEY")
assert runtime_api_key, "RUNTIME_API_KEY required"

# If GITHUB_SHA is set (e.g. running in CI of a PR), use that to ensure consistency
# Otherwise, use the latest image from main
server_image_sha = os.getenv("GITHUB_SHA") or "main"
# SDK_SHA is the canonical commit SHA set by CI workflows (avoids the
# built-in GITHUB_SHA which resolves to the merge-commit on PRs).
server_image_sha = os.getenv("SDK_SHA") or os.getenv("GITHUB_SHA") or "main"
server_image = f"ghcr.io/openhands/agent-server:{server_image_sha[:7]}-python-amd64"
logger.info(f"Using server image: {server_image}")

Expand Down
121 changes: 16 additions & 105 deletions sdk/guides/agent-delegation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
Agent delegation allows a main agent to spawn multiple sub-agents and delegate tasks to them for parallel processing. Each sub-agent runs independently with its own conversation context and returns results that the main agent can consolidate and process further.

This pattern is useful when:
- Breaking down complex problems into independent subtasks

Check warning on line 15 in sdk/guides/agent-delegation.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-delegation.mdx#L15

Did you really mean 'subtasks'?
- Processing multiple related tasks in parallel
- Separating concerns between different specialized sub-agents
- Improving throughput for parallelizable work

Check warning on line 18 in sdk/guides/agent-delegation.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-delegation.mdx#L18

Did you really mean 'parallelizable'?

## How It Works

Expand Down Expand Up @@ -179,15 +179,13 @@
from openhands.sdk.context import Skill
from openhands.sdk.subagent import register_agent
from openhands.sdk.tool import register_tool
from openhands.tools import register_builtins_agents
from openhands.tools.delegate import (
DelegateTool,
DelegationVisualizer,
)
from openhands.tools.preset.default import get_default_tools, register_builtins_agents


ONLY_RUN_SIMPLE_DELEGATION = False

logger = get_logger(__name__)

# Configure LLM and agent
Expand All @@ -198,91 +196,6 @@
usage_id="agent",
)

cwd = os.getcwd()

tools = get_default_tools(enable_browser=True)
tools.append(Tool(name=DelegateTool.name))
register_builtins_agents()

main_agent = Agent(
llm=llm,
tools=tools,
)
conversation = Conversation(
agent=main_agent,
workspace=cwd,
visualizer=DelegationVisualizer(name="Delegator"),
)

conversation.send_message(
"Forget about coding. Let's switch to travel planning. "
"Let's plan a trip to London. I have two issues I need to solve: "
"Lodging: what are the best areas to stay at while keeping budget in mind? "
"Activities: what are the top 5 must-see attractions and hidden gems? "
"Please use the delegation tools to handle these two tasks in parallel. "
"Make sure the sub-agents use their own knowledge "
"and dont rely on internet access. "
"They should keep it short. After getting the results, merge both analyses "
"into a single consolidated report.\n\n"
)
conversation.run()

conversation.send_message(
"Ask the lodging sub-agent what it thinks about Covent Garden."
)
conversation.run()

# Report cost for simple delegation example
cost_simple = conversation.conversation_stats.get_combined_metrics().accumulated_cost
print(f"EXAMPLE_COST (simple delegation): {cost_simple}")

print("Simple delegation example done!", "\n" * 20)

if ONLY_RUN_SIMPLE_DELEGATION:
# For CI: always emit the EXAMPLE_COST marker before exiting.
print(f"EXAMPLE_COST: {cost_simple}")
exit(0)


# -------- Agent Delegation Second Part: Built-in Agent Types (Explore + Bash) --------

main_agent = Agent(
llm=llm,
tools=[Tool(name=DelegateTool.name)],
)
conversation = Conversation(
agent=main_agent,
workspace=cwd,
visualizer=DelegationVisualizer(name="Delegator (builtins)"),
)

builtin_task_message = (
"Demonstrate SDK built-in sub-agent types. "
"1) Spawn an 'explore' sub-agent and ask it to list the markdown files in "
"openhands-sdk/openhands/sdk/subagent/builtins/ and summarize what each "
"built-in agent type is for (based on the file contents). "
"2) Spawn a 'bash' sub-agent and ask it to run `python --version` in the "
"terminal and return the exact output. "
"3) Merge both results into a short report. "
"Do not use internet access."
)

print("=" * 100)
print("Demonstrating built-in agent delegation (explore + bash)...")
print("=" * 100)

conversation.send_message(builtin_task_message)
conversation.run()

# Report cost for builtin agent types example
cost_builtin = conversation.conversation_stats.get_combined_metrics().accumulated_cost
print(f"EXAMPLE_COST (builtin agents): {cost_builtin}")

print("Built-in agent delegation example done!", "\n" * 20)


# -------- Agent Delegation Third Part: User-Defined Agent Types --------


def create_lodging_planner(llm: LLM) -> Agent:
"""Create a lodging planner focused on London stays."""
Expand Down Expand Up @@ -343,6 +256,7 @@
factory_func=create_activities_planner,
description="Creates time-efficient London activity itineraries.",
)
register_builtins_agents()

# Make the delegation tool available to the main agent
register_tool("DelegateTool", DelegateTool)
Expand All @@ -353,26 +267,26 @@
)
conversation = Conversation(
agent=main_agent,
workspace=cwd,
workspace=os.getcwd(),
visualizer=DelegationVisualizer(name="Delegator"),
)

task_message = (
"Plan a 3-day London trip. "
"1) Spawn two sub-agents: lodging_planner (hotel options) and "
"activities_planner (itinerary). "
"2) Ask lodging_planner for 3-4 central London hotel recommendations with "
"neighborhoods, quick pros/cons, and transit notes by budget. "
"3) Ask activities_planner for a concise 3-day itinerary with nearby stops, "
" food/coffee suggestions, and any ticket/reservation notes. "
"4) Share both sub-agent results and propose a combined plan."
)

print("=" * 100)
print("Demonstrating London trip delegation (lodging + activities)...")
print("=" * 100)

conversation.send_message(task_message)
conversation.send_message("""
Let's plan a trip to London. I have two specific areas to address:

Lodging: What are the best areas to stay in while keeping a budget in mind?
Activities: What are the top five must-see attractions and hidden gems?

Please use delegation tools to handle these two tasks in parallel.
Ensure the sub-agents use their own internal knowledge and do not
rely on internet access. Keep the responses concise.
Once you have the results, use the bash sub-agent to write a file
named london_trip_report.txt containing the findings in the working directory.
""")
conversation.run()

conversation.send_message(
Expand All @@ -384,12 +298,9 @@
cost_user_defined = (
conversation.conversation_stats.get_combined_metrics().accumulated_cost
)
print(f"EXAMPLE_COST (user-defined agents): {cost_user_defined}")
print(f"EXAMPLE_COST: {cost_user_defined}")

print("All done!")

# Full example cost report for CI workflow
print(f"EXAMPLE_COST: {cost_simple + cost_builtin + cost_user_defined}")
```

<RunExampleCode path_to_script="examples/01_standalone_sdk/25_agent_delegation.py"/>
2 changes: 1 addition & 1 deletion sdk/guides/agent-file-based.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: File-Based Agents
description: Define specialized sub-agents as simple Markdown files with YAML frontmatter — no Python code required.

Check warning on line 3 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L3

Did you really mean 'frontmatter'?
---

import RunExampleCode from "/sdk/shared-snippets/how-to-run-example.mdx";
Expand All @@ -13,7 +13,7 @@

## Agent File Format

An agent is a single `.md` file with YAML frontmatter and a Markdown body:

Check warning on line 16 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L16

Did you really mean 'frontmatter'?

```markdown icon="markdown"
---
Expand All @@ -40,9 +40,9 @@
Keep feedback concise and actionable. For each issue, suggest a fix.
```

The YAML frontmatter configures the agent. The Markdown body becomes the agent's system prompt.

Check warning on line 43 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L43

Did you really mean 'frontmatter'?

### Frontmatter Fields

Check warning on line 45 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L45

Did you really mean 'Frontmatter'?

| Field | Required | Default | Description |
|-------|----------|---------|-------------|
Expand Down Expand Up @@ -174,23 +174,23 @@
print(f"Registered {len(agent_names)} agents: {agent_names}")
```

This scans both project-level and user-level directories, deduplicates by name, and registers each agent as a delegate that can be spawned by the orchestrator.

Check warning on line 177 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L177

Did you really mean 'deduplicates'?

## Manual Loading

For more control, load and register agents explicitly:

```python icon="python" focus={3-6, 8-14}
from pathlib import Path

Check warning on line 184 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L184

Did you really mean 'pathlib'?

from openhands.sdk import load_agents_from_dir, register_agent, agent_definition_to_factory

# Load from a specific directory
agents_dir = Path("agents")

Check warning on line 189 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L189

Did you really mean 'agents_dir'?
agent_definitions = load_agents_from_dir(agents_dir)

Check warning on line 190 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L190

Did you really mean 'agent_definitions'?

# Register each agent
for agent_def in agent_definitions:

Check warning on line 193 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L193

Did you really mean 'agent_def'?

Check warning on line 193 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L193

Did you really mean 'agent_definitions'?
register_agent(
name=agent_def.name,
factory_func=agent_definition_to_factory(agent_def),
Expand Down Expand Up @@ -226,7 +226,7 @@
```

The factory:
- Maps tool names from the frontmatter to `Tool` objects

Check warning on line 229 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L229

Did you really mean 'frontmatter'?
- Appends the Markdown body to the parent system message via `AgentContext(system_message_suffix=...)`
- Respects the `model` field (`"inherit"` keeps the parent LLM; an explicit model name creates a copy)

Expand All @@ -237,8 +237,8 @@
```python icon="python" focus={3, 4}
from openhands.sdk.subagent import load_project_agents, load_user_agents

project_agents = load_project_agents("/path/to/project")

Check warning on line 240 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L240

Did you really mean 'project_agents'?
user_agents = load_user_agents() # scans ~/.agents/agents/ and ~/.openhands/agents/

Check warning on line 241 in sdk/guides/agent-file-based.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-file-based.mdx#L241

Did you really mean 'user_agents'?
```

## Using with Delegation
Expand Down Expand Up @@ -518,7 +518,7 @@
register_agent(
name=grammar_checker.name,
factory_func=agent_definition_to_factory(grammar_checker),
description=grammar_checker.description,
description=grammar_checker,
)

# 3. Set up the orchestrator agent with the DelegateTool
Expand Down
6 changes: 3 additions & 3 deletions sdk/guides/agent-server/api-sandbox.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
The [Runtime API](https://runtime.all-hands.dev/) (`runtime.all-hands.dev`) is designed primarily for **[benchmark evaluation at scale](https://github.com/OpenHands/benchmarks)**, not for building production applications. If you are building a production application with the SDK, use the **[OpenHands Cloud Workspace](/sdk/guides/agent-server/cloud-workspace)** instead, which provides fully managed sandbox environments with SaaS credential support.
</Warning>

The API-sandboxed agent server demonstrates how to use `APIRemoteWorkspace` to connect to a [OpenHands runtime API service](https://runtime.all-hands.dev/). This eliminates the need to manage your own infrastructure, providing automatic scaling, monitoring, and secure sandboxed execution.

Check warning on line 12 in sdk/guides/agent-server/api-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/api-sandbox.mdx#L12

Did you really mean 'sandboxed'?

## Key Concepts

Expand All @@ -27,7 +27,7 @@

This workspace type:
- Connects to a remote runtime API service
- Automatically provisions sandboxed environments

Check warning on line 30 in sdk/guides/agent-server/api-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/api-sandbox.mdx#L30

Did you really mean 'sandboxed'?
- Manages container lifecycle through the API
- Handles all infrastructure concerns

Expand Down Expand Up @@ -151,9 +151,9 @@
exit(1)


# If GITHUB_SHA is set (e.g. running in CI of a PR), use that to ensure consistency
# Otherwise, use the latest image from main
server_image_sha = os.getenv("GITHUB_SHA") or "main"
# SDK_SHA is the canonical commit SHA set by CI workflows (avoids the
# built-in GITHUB_SHA which resolves to the merge-commit on PRs).
server_image_sha = os.getenv("SDK_SHA") or os.getenv("GITHUB_SHA") or "main"
server_image = f"ghcr.io/openhands/agent-server:{server_image_sha[:7]}-python-amd64"
logger.info(f"Using server image: {server_image}")

Expand Down
12 changes: 6 additions & 6 deletions sdk/guides/agent-server/apptainer-sandbox.mdx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
---
title: Apptainer Sandbox

Check warning on line 2 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L2

Did you really mean 'Apptainer'?
description: Run agent server in rootless Apptainer containers for HPC and shared computing environments.

Check warning on line 3 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L3

Did you really mean 'Apptainer'?
---

import RunExampleCode from "/sdk/shared-snippets/how-to-run-example.mdx";

> A ready-to-run example is available [here](#basic-apptainer-sandbox-example)!

The Apptainer sandboxed agent server demonstrates how to run agents in isolated Apptainer containers using ApptainerWorkspace.

Check warning on line 10 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L10

Did you really mean 'Apptainer'?

Check warning on line 10 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L10

Did you really mean 'sandboxed'?

Check warning on line 10 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L10

Did you really mean 'Apptainer'?

Apptainer (formerly Singularity) is a container runtime designed for HPC environments that doesn't require root access, making it ideal for shared computing environments, university clusters, and systems where Docker is not available.

Check warning on line 12 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L12

Did you really mean 'Apptainer'?

## When to Use Apptainer

Check warning on line 14 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L14

Did you really mean 'Apptainer'?

Use Apptainer instead of Docker when:

Check warning on line 16 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L16

Did you really mean 'Apptainer'?
- Running on HPC clusters or shared computing environments
- Root access is not available
- Docker daemon cannot be installed
Expand All @@ -23,16 +23,16 @@
## Prerequisites

Before running this example, ensure you have:
- Apptainer installed ([Installation Guide](https://apptainer.org/docs/user/main/quick_start.html))

Check warning on line 26 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L26

Did you really mean 'Apptainer'?
- LLM API key set in environment

## Basic Apptainer Sandbox Example

Check warning on line 29 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L29

Did you really mean 'Apptainer'?

<Note>
This example is available on GitHub: [examples/02_remote_agent_server/08_convo_with_apptainer_sandboxed_server.py](https://github.com/OpenHands/software-agent-sdk/blob/main/examples/02_remote_agent_server/08_convo_with_apptainer_sandboxed_server.py)
</Note>

This example shows how to create an `ApptainerWorkspace` that automatically manages Apptainer containers for agent execution:

Check warning on line 35 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L35

Did you really mean 'Apptainer'?

```python icon="python" expandable examples/02_remote_agent_server/08_convo_with_apptainer_sandboxed_server.py
import os
Expand Down Expand Up @@ -77,11 +77,11 @@
"""Get the server image tag, using PR-specific image in CI."""
platform_str = detect_platform()
arch = "arm64" if "arm64" in platform_str else "amd64"
# If GITHUB_SHA is set (e.g. running in CI of a PR), use that to ensure consistency
# Otherwise, use the latest image from main
github_sha = os.getenv("GITHUB_SHA")
if github_sha:
return f"ghcr.io/openhands/agent-server:{github_sha[:7]}-python-{arch}"
# SDK_SHA is the canonical commit SHA set by CI workflows (avoids the
# built-in GITHUB_SHA which resolves to the merge-commit on PRs).
sha = os.getenv("SDK_SHA") or os.getenv("GITHUB_SHA")
if sha:
return f"ghcr.io/openhands/agent-server:{sha[:7]}-python-{arch}"
return "ghcr.io/openhands/agent-server:latest-python"


Expand All @@ -95,7 +95,7 @@
with ApptainerWorkspace(
# use pre-built image for faster startup
server_image=server_image,
host_port=8010,
# host_port auto-selects an available port when not specified
platform=detect_platform(),
) as workspace:
# 3) Create agent
Expand Down Expand Up @@ -196,7 +196,7 @@

### Option 3: Use Existing SIF File

If you have a pre-built Apptainer SIF file:

Check warning on line 199 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L199

Did you really mean 'Apptainer'?

```python icon="python" focus={2}
with ApptainerWorkspace(
Expand Down Expand Up @@ -237,7 +237,7 @@

While the API is similar to DockerWorkspace, there are some differences:

| Feature | Docker | Apptainer |

Check warning on line 240 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L240

Did you really mean 'Apptainer'?
|---------|--------|-----------|
| Root access required | Yes (daemon) | No |
| Installation | Requires Docker Engine | Single binary |
Expand All @@ -248,21 +248,21 @@

## Troubleshooting

### Apptainer Not Found

Check warning on line 251 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L251

Did you really mean 'Apptainer'?

If you see `apptainer: command not found`:
1. Install Apptainer following the [official guide](https://apptainer.org/docs/user/main/quick_start.html)

Check warning on line 254 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L254

Did you really mean 'Apptainer'?
2. Ensure it's in your PATH: `which apptainer`

### Permission Errors

Apptainer should work without root. If you see permission errors:

Check warning on line 259 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L259

Did you really mean 'Apptainer'?
- Check that your user has access to `/tmp`
- Verify Apptainer is properly installed: `apptainer version`

Check warning on line 261 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L261

Did you really mean 'Apptainer'?
- Ensure the cache directory is writable: `ls -la ~/.cache/apptainer/`

## Next Steps

- **[Docker Sandbox](/sdk/guides/agent-server/docker-sandbox)** - Alternative container runtime
- **[API Sandbox](/sdk/guides/agent-server/api-sandbox)** - Remote API-based sandboxing

Check warning on line 267 in sdk/guides/agent-server/apptainer-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/apptainer-sandbox.mdx#L267

Did you really mean 'sandboxing'?
- **[Local Server](/sdk/guides/agent-server/local-server)** - Non-sandboxed local execution
7 changes: 3 additions & 4 deletions sdk/guides/agent-server/cloud-workspace.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

> A ready-to-run example is available [here](#ready-to-run-example)!

The `OpenHandsCloudWorkspace` demonstrates how to use the [OpenHands Cloud](https://app.all-hands.dev) to provision and manage sandboxed environments for agent execution. This provides a seamless experience with automatic sandbox provisioning, monitoring, and secure execution without managing your own infrastructure.

Check warning on line 8 in sdk/guides/agent-server/cloud-workspace.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/cloud-workspace.mdx#L8

Did you really mean 'sandboxed'?

## Key Concepts

Expand Down Expand Up @@ -71,7 +71,7 @@

```python icon="python" focus={1-3}
result = workspace.execute_command(
"echo 'Hello from OpenHands Cloud sandbox!' && pwd"

Check warning on line 74 in sdk/guides/agent-server/cloud-workspace.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/cloud-workspace.mdx#L74

Did you really mean 'pwd'?
)
logger.info(f"Command completed: {result.exit_code}, {result.stdout}")
```
Expand Down Expand Up @@ -310,10 +310,9 @@
cloud_api_key=cloud_api_key,
) as workspace:
# --- LLM from SaaS account settings ---
# get_llm() calls GET /users/me?expose_secrets=true,
# sending your Cloud API key plus the sandbox session
# key that OpenHands Cloud issued for this workspace.
# It returns a fully configured LLM instance.
# get_llm() calls GET /users/me?expose_secrets=true
# (dual auth: Bearer + session key) and returns a
# fully configured LLM instance.
# Override any parameter: workspace.get_llm(model="gpt-4o")
llm = workspace.get_llm()
logger.info(f"LLM configured: model={llm.model}")
Expand Down
38 changes: 19 additions & 19 deletions sdk/guides/agent-server/docker-sandbox.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
---
import RunExampleCode from "/sdk/shared-snippets/how-to-run-example.mdx";

The docker sandboxed agent server demonstrates how to run agents in isolated Docker containers using `DockerWorkspace`.

Check warning on line 7 in sdk/guides/agent-server/docker-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/docker-sandbox.mdx#L7

Did you really mean 'sandboxed'?

This provides complete isolation from the host system, making it ideal for production deployments, testing, and executing untrusted code safely.

Check warning on line 9 in sdk/guides/agent-server/docker-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/docker-sandbox.mdx#L9

Did you really mean 'untrusted'?

Use `DockerWorkspace` with a pre-built agent server image for the fastest startup. When you need to build your own image from a base image, switch to `DockerDevWorkspace`.

Expand Down Expand Up @@ -160,11 +160,11 @@
"""Get the server image tag, using PR-specific image in CI."""
platform_str = detect_platform()
arch = "arm64" if "arm64" in platform_str else "amd64"
# If GITHUB_SHA is set (e.g. running in CI of a PR), use that to ensure consistency
# Otherwise, use the latest image from main
github_sha = os.getenv("GITHUB_SHA")
if github_sha:
return f"ghcr.io/openhands/agent-server:{github_sha[:7]}-python-{arch}"
# SDK_SHA is the canonical commit SHA set by CI workflows (avoids the
# built-in GITHUB_SHA which resolves to the merge-commit on PRs).
sha = os.getenv("SDK_SHA") or os.getenv("GITHUB_SHA")
if sha:
return f"ghcr.io/openhands/agent-server:{sha[:7]}-python-{arch}"
return "ghcr.io/openhands/agent-server:latest-python"


Expand All @@ -173,7 +173,7 @@
# image or `DockerDevWorkspace` to automatically build the image on-demand.
# with DockerDevWorkspace(
# # dynamically build agent-server image
# base_image="nikolaik/python-nodejs:python3.13-nodejs22",
# base_image="nikolaik/python-nodejs:python3.13-nodejs22-slim",
# host_port=8010,
# platform=detect_platform(),
# ) as workspace:
Expand All @@ -182,7 +182,7 @@
with DockerWorkspace(
# use pre-built image for faster startup
server_image=server_image,
host_port=8010,
# host_port auto-selects an available port when not specified
platform=detect_platform(),
) as workspace:
# 3) Create agent
Expand Down Expand Up @@ -313,7 +313,7 @@
http://localhost:{vscode_port}/?tkn={token}&folder={workspace_dir}
```
where:
- `vscode_port`: Usually host_port + 1 (e.g., 8011)

Check warning on line 316 in sdk/guides/agent-server/docker-sandbox.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-server/docker-sandbox.mdx#L316

Did you really mean 'host_port'?
- `token`: Authentication token for security
- `workspace_dir`: Workspace directory to open

Expand Down Expand Up @@ -364,11 +364,11 @@
"""Get the server image tag, using PR-specific image in CI."""
platform_str = detect_platform()
arch = "arm64" if "arm64" in platform_str else "amd64"
# If GITHUB_SHA is set (e.g. running in CI of a PR), use that to ensure consistency
# Otherwise, use the latest image from main
github_sha = os.getenv("GITHUB_SHA")
if github_sha:
return f"ghcr.io/openhands/agent-server:{github_sha[:7]}-python-{arch}"
# SDK_SHA is the canonical commit SHA set by CI workflows (avoids the
# built-in GITHUB_SHA which resolves to the merge-commit on PRs).
sha = os.getenv("SDK_SHA") or os.getenv("GITHUB_SHA")
if sha:
return f"ghcr.io/openhands/agent-server:{sha[:7]}-python-{arch}"
return "ghcr.io/openhands/agent-server:latest-python"


Expand Down Expand Up @@ -561,11 +561,11 @@
"""Get the server image tag, using PR-specific image in CI."""
platform_str = detect_platform()
arch = "arm64" if "arm64" in platform_str else "amd64"
# If GITHUB_SHA is set (e.g. running in CI of a PR), use that to ensure consistency
# Otherwise, use the latest image from main
github_sha = os.getenv("GITHUB_SHA")
if github_sha:
return f"ghcr.io/openhands/agent-server:{github_sha[:7]}-python-{arch}"
# SDK_SHA is the canonical commit SHA set by CI workflows (avoids the
# built-in GITHUB_SHA which resolves to the merge-commit on PRs).
sha = os.getenv("SDK_SHA") or os.getenv("GITHUB_SHA")
if sha:
return f"ghcr.io/openhands/agent-server:{sha[:7]}-python-{arch}"
return "ghcr.io/openhands/agent-server:latest-python"


Expand All @@ -574,15 +574,15 @@
# automatically build the image on-demand.
# with DockerDevWorkspace(
# # dynamically build agent-server image
# base_image="nikolaik/python-nodejs:python3.13-nodejs22",
# base_image="nikolaik/python-nodejs:python3.13-nodejs22-slim",
# host_port=8010,
# platform=detect_platform(),
# ) as workspace:
server_image = get_server_image()
logger.info(f"Using server image: {server_image}")
with DockerWorkspace(
server_image=server_image,
host_port=8011,
# host_port auto-selects an available port when not specified
platform=detect_platform(),
extra_ports=True, # Expose extra ports for VSCode and VNC
) as workspace:
Expand Down
Loading
Loading