Skip to content

feat: Add ACPAgent for Agent Client Protocol integration#2133

Merged
simonrosenberg merged 11 commits intomainfrom
feat/acp-agent
Feb 20, 2026
Merged

feat: Add ACPAgent for Agent Client Protocol integration#2133
simonrosenberg merged 11 commits intomainfrom
feat/acp-agent

Conversation

@simonrosenberg
Copy link
Collaborator

@simonrosenberg simonrosenberg commented Feb 19, 2026

Summary

  • Add ACPAgent, an AgentBase subclass that delegates to ACP-compatible servers (Claude Code, Gemini CLI, etc.) instead of direct LLM calls
  • The ACP server manages its own LLM, tools, and execution lifecycle; ACPAgent simply relays user messages and collects responses
  • Includes LLM telemetry: per-turn token usage from PromptResponse.usage, incremental cost from UsageUpdate notifications, and stats callback for GUI updates
  • Includes stdout filtering for claude-code-acp v0.1.x which emits non-JSON-RPC log lines to stdout
  • Optional dependency: pip install 'openhands-sdk[acp]' (requires agent-client-protocol>=0.8.1)

Changes

File Description
openhands-sdk/openhands/sdk/agent/acp_agent.py Main implementation: _OpenHandsACPClient, _filter_jsonrpc_lines, ACPAgent with telemetry
openhands-sdk/openhands/sdk/agent/__init__.py Lazy import for ACPAgent to avoid requiring agent-client-protocol at import time
openhands-sdk/pyproject.toml Add acp optional dependency
tests/sdk/agent/test_acp_agent.py 47 unit tests covering instantiation, serialization, validation, step, cleanup, JSON-RPC filtering, and telemetry
examples/01_standalone_sdk/36_acp_agent_example.py Minimal integration example with try/finally cleanup

Test plan

  • 47 unit tests pass (uv run pytest tests/sdk/agent/test_acp_agent.py -v)
  • Ruff lint passes on all modified/created files
  • Integration example runs end-to-end with real claude-code-acp server

Closes #590

🤖 Generated with Claude Code


Agent Server images for this PR

GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python amd64, arm64 nikolaik/python-nodejs:python3.12-nodejs22 Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:7154df8-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-7154df8-python \
  ghcr.io/openhands/agent-server:7154df8-python

All tags pushed for this build

ghcr.io/openhands/agent-server:7154df8-golang-amd64
ghcr.io/openhands/agent-server:7154df8-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:7154df8-golang-arm64
ghcr.io/openhands/agent-server:7154df8-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:7154df8-java-amd64
ghcr.io/openhands/agent-server:7154df8-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:7154df8-java-arm64
ghcr.io/openhands/agent-server:7154df8-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:7154df8-python-amd64
ghcr.io/openhands/agent-server:7154df8-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-amd64
ghcr.io/openhands/agent-server:7154df8-python-arm64
ghcr.io/openhands/agent-server:7154df8-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-arm64
ghcr.io/openhands/agent-server:7154df8-golang
ghcr.io/openhands/agent-server:7154df8-java
ghcr.io/openhands/agent-server:7154df8-python

About Multi-Architecture Support

  • Each variant tag (e.g., 7154df8-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., 7154df8-python-amd64) are also available if needed

Documentation

simonrosenberg and others added 2 commits February 19, 2026 16:45
Add ACPAgent, an AgentBase subclass that delegates to ACP-compatible
servers (Claude Code, Gemini CLI, etc.) instead of direct LLM calls.
The ACP server manages its own LLM, tools, and execution lifecycle.

Closes #590

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Record per-turn token usage from PromptResponse.usage in step()
- Record incremental cost from UsageUpdate notifications in session_update()
- Yield sentinel LLM from get_all_llms() for telemetry pipeline
- Wire _llm_ref on client for dynamic metrics/telemetry access
- Trigger stats callback after each step for GUI updates
- Extract notification drain delay to configurable constant
- Add audit logging for auto-approved permission requests
- Add debug logging in cleanup for process/executor errors
- Wrap example in try/finally for robust cleanup
- Add 8 telemetry tests in TestACPAgentTelemetry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Collaborator

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Good taste - Clean integration with pragmatic workarounds for ecosystem quirks. Solid testing and good defensive error handling.

…ition

Add ACP_NOTIFICATION_DRAIN_DELAY env var override, yield to the event
loop before the timed sleep, and add a TODO for protocol-level fix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 19, 2026

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-sdk/openhands/sdk/agent
   __init__.py10460%17–18, 20–21
   acp_agent.py2345775%100–102, 136–143, 145–146, 156–157, 159, 170–171, 176, 243, 309, 319, 324, 335–338, 351–353, 356–358, 360–361, 363, 365, 370, 378–379, 383–384, 388, 395, 398–399, 401, 403–404, 439, 446–448, 474–475, 544–545
TOTAL19385529772% 

simonrosenberg and others added 3 commits February 19, 2026 19:53
- Fix RequestPermissionResponse constructor (outcome, not result)
- Add proper signatures to Client protocol stub methods
- Narrow content[0] type in tests with isinstance checks
- Pass required args in fs/terminal stub tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Renumber example 36 -> 40 to avoid conflict with
  36_event_json_to_openai_messages.py
- Add type: ignore[reportMissingImports] to all acp imports (optional dep)
- Add TYPE_CHECKING import in __init__.py to satisfy pyright __all__ check
- Add @requires_acp skip marker for tests needing the acp package

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move acp from optional to required deps, removing:
- Lazy import guard in init_state
- type: ignore[reportMissingImports] comments
- @requires_acp skip markers on tests
- test_missing_acp_sdk_raises_import_error test

The __getattr__ in __init__.py is kept to avoid a circular import.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@neubig neubig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think overall it looks good, I left a few comments. I assume that this has been tested, in which case we can probably merge it in and then iterate.

simonrosenberg and others added 2 commits February 20, 2026 15:19
Co-authored-by: Engel Nyst <engel.nyst@gmail.com>
Co-authored-by: Engel Nyst <engel.nyst@gmail.com>
@simonrosenberg simonrosenberg force-pushed the feat/acp-agent branch 4 times, most recently from 934a9d8 to d5c43b1 Compare February 20, 2026 19:24
- Rename _make_sentinel_llm → _make_dummy_llm
- Move inline acp imports to top-level
- Add GitHub issue link to TODO comment
- Simplify _filter_jsonrpc_lines exception handling
- Add debug logging for on_token and stats callback failures
- Remove chatty Example from class docstring
- Remove critic validation check (ACP server manages its own evaluation)
- Add comment explaining lazy import in __init__.py (avoids circular dependency)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@simonrosenberg simonrosenberg merged commit fc84a48 into main Feb 20, 2026
27 of 28 checks passed
@simonrosenberg simonrosenberg deleted the feat/acp-agent branch February 20, 2026 22:06
@xingyaoww
Copy link
Collaborator

@simonrosenberg btw, this workflow is broken. We should also add a docs for this script on OpenHands/docs repo:

https://github.com/OpenHands/software-agent-sdk/actions/runs/22243244271/job/64351561891?pr=2160

simonrosenberg added a commit that referenced this pull request Feb 21, 2026
Resolve conflicts between TCP transport feature and upstream changes
(ACPAgent refactoring from #2133, ask_agent from #2145).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jpshackelford
Copy link
Contributor

jpshackelford commented Mar 6, 2026

CLI Compatibility Note

This PR introduced breaking changes to the Agent class that are incompatible with the current OpenHands-CLI:

  • New abstract methods required: set_config_option, fork_session, etc.
  • Method signature changes in new_session() and load_session()
  • Import changes in acp.schema (StdioMcpServerMcpServerStdio, removed SessionUpdate2-6)
  • agent-client-protocol bumped from <0.8.0 to >=0.8.1

CLI fix PR: OpenHands/OpenHands-CLI#548

Question: Are subsequent SDK fixes/features intended for CLI use now blocked until the CLI PR #548 is merged? For example, we're trying to test #2334 (terminal escape filter fix) with the CLI, but it fails because it's based on post-breaking-change main.

cc @enyst

@enyst
Copy link
Collaborator

enyst commented Mar 6, 2026

@OpenHands We see this error. Check if that example is still undocumented, and if so, do what it says below. Verify in the sync workflows on the docs/ repo in the same org to understand CI on this, so you know:

❌ Found 1 undocumented example(s):

  • examples/01_standalone_sdk/40_acp_agent_example.py

⚠️ Please add documentation for these examples in the docs repo.

📚 How to Document Examples:

  1. Clone the docs repository:
    git clone https://github.com/OpenHands/docs.git

  2. Create a new .mdx file in sdk/guides/ directory
    (e.g., sdk/guides/my-feature.mdx)

  3. Add the example code block with this format:

    <code will be auto-synced>
  4. See the format documentation at:
    https://github.com/OpenHands/docs/blob/main/.github/scripts/README.md

  5. Example documentation files can be found in:
    https://github.com/OpenHands/docs/tree/main/sdk/guides

  6. After creating the PR in docs repo, reference it in your
    agent-sdk PR description.
    ============================================================
    Error: Process completed with exit code 1.

@openhands-ai
Copy link

openhands-ai bot commented Mar 6, 2026

I'm on it! enyst can track my progress at all-hands.dev

@openhands-ai

This comment was marked as duplicate.

@enyst
Copy link
Collaborator

enyst commented Mar 6, 2026

@OpenHands Read this comment #2133 (comment) and verify each link.

Look at our workflow with griffe on api breakage. Did that happen in this PR? If not, why not? Were these methods referenced in the public api? (if not, do you think they should be, wdyt?)

Then. We just made a release of the SDK, find the release PR.

Clone yourself the CLI repo and verify on which SDK version it is. If we upgrade it, will the comment still apply?

Make a new investigation issue in the sdk repo, and answer my questions there, plus any results of your investigation. Answer the questions in the comment linked, too.

@openhands-ai
Copy link

openhands-ai bot commented Mar 6, 2026

I'm on it! enyst can track my progress at all-hands.dev

@openhands-ai
Copy link

openhands-ai bot commented Mar 6, 2026

Final summary of work:

No code changes were made; therefore there is nothing to push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement a special ACPAgent which is a Agent-Client Protocol Client?

6 participants