Skip to content
Merged
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
65 changes: 63 additions & 2 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 All @@ -29,19 +29,71 @@
**Key difference from standard agents:** With `ACPAgent`, you don't need an `LLM_API_KEY` in your code. The ACP server handles its own LLM authentication and API calls. This is *delegation* — your code sends messages to the ACP server, which manages all LLM interactions internally.
</Note>

### Prompt Context (AgentContext)

`ACPAgent` supports `agent_context` for **prompt-only extensions** — skills, repository context, current datetime, and system/user message suffixes are appended to the user message before it reaches the ACP server. This lets you inject the same skill catalog and repo-specific guidance that the built-in Agent receives, without interfering with the server's own tools or execution model.

```python icon="python" highlight={4-12,16}
from openhands.sdk.agent import ACPAgent
from openhands.sdk import AgentContext
from openhands.sdk.context import Skill

context = AgentContext(
skills=[
Skill(
name="code-style",
content="Always use type hints in Python.",
trigger=None, # always active
),
],
system_message_suffix="You are reviewing a Python project.",
)

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

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>
`user_message_suffix` is an ACP-compatible field, but it is **not** duplicated in `to_acp_prompt_context()` because the conversation layer already applies it through `MessageEvent.to_llm_message()`.
</Note>

#### Compatible AgentContext Fields

Each `AgentContext` field is tagged as ACP-compatible or not. At initialization, `validate_acp_compatibility()` rejects any context that uses unsupported fields.

| Field | ACP Compatible | Notes |
|-------|:-:|-------|
| `skills` | ✅ | Skill catalog and trigger-based injections |
| `system_message_suffix` | ✅ | Appended to the prompt context |
| `user_message_suffix` | ✅ | Applied by the conversation layer |
| `current_datetime` | ✅ | Included in the rendered prompt |
| `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 and context, these `AgentBase` features are not available on `ACPAgent`:
Because the ACP server manages its own tools, context window, and execution, these `AgentBase` features are not available on `ACPAgent`:

- `tools` / `include_default_tools` — the server has its own tools
- `mcp_config` — configure MCP on the server side
- `condenser` — the server manages its own context window
- `critic` — the server manages its own evaluation
- `agent_context` — configure the server directly

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 @@ -59,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 All @@ -82,6 +134,15 @@
| `acp_args` | Additional arguments appended to the command |
| `acp_env` | Additional environment variables for the server process |

### Authentication

When the ACP server advertises authentication methods, `ACPAgent` automatically selects a credential source:

1. **ChatGPT subscription login** — If the server supports a `chatgpt` auth method and `~/.codex/auth.json` exists (created by `LLM.subscription_login()`), this is selected first. This enables ACP-backed workflows to use device-code login credentials without an explicit API key.
2. **API key environment variables** — Falls back to checking for `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or `GEMINI_API_KEY` depending on which auth methods the server supports.

If no supported credential source is found, the server may proceed without authentication (some servers don't require it).

## Metrics

Token usage and cost data are automatically captured from the ACP server's responses. You can inspect them through the standard `LLM.metrics` interface:
Expand All @@ -100,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 Down Expand Up @@ -187,7 +248,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 251 in sdk/guides/agent-acp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-acp.mdx#L251

Did you really mean 'ACPAgent'?

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

View check run for this annotation

Mintlify / Mintlify Validation (allhandsai) - vale-spellcheck

sdk/guides/agent-acp.mdx#L251

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
Loading