Skip to main content

langgraph-acp

Use ACP-compatible agents — Codex, Claude, and anything else that speaks the protocol — as first-class LangGraph nodes.

LangGraph owns orchestration and workflow durability.
ACP owns the agent conversation and agent-side session state.
langgraph-acp owns the binding between the two.

The design and the ticket sequence live in docs/langgraph-acp Architecture and Implementation Plan.md.

Status

Tickets 1 to 4 of that plan: the core types, a connection to a real agent, the store that remembers which conversation belongs to which node, and the first working ACPNode end-to-end path.

from langgraph_acp import ACPNode

result = await ACPNode(agent="codex")("Review this change")
print(result.message)

Available now:

ACPNode a LangGraph-compatible async callable that runs one ACP turn
ACPAgentProvider, ACPAgentRegistry, default_registry names resolve to agents
StdioACPProvider, CodexACPProvider how an agent is launched
ACPClient, ACPCapabilities one live connection, and what it can do
ACPSession one conversation, and the turns run in it
ACPSessionSpec, ACPSessionStrategy which conversation a node speaks in
ACPSessionStore, InMemoryACPSessionStore which ACP session a node's identity resolves to
ACPWorkspace the filesystem context a session is given
ACPConfig, ACPRequirements settings requested, capabilities demanded
ACPEvent, ACPEventType streamed activity, normalized
ACPResult, ACPUsage what a turn returns, and what it cost
ACPError and its subclasses typed failures

Registering another ACP agent is a command line rather than a class:

default_registry().register(
    StdioACPProvider(name="gemini", command=["gemini", "--experimental-acp"])
)

The minimal node takes its invocation input as the prompt and starts a fresh session each time. Runtime prompt resolvers, session reuse, MCP injection, and LangGraph event streaming arrive in the following tickets.

Session bindings

ACPSessionStore maps a node's logical identity to the ACP session it speaks in, so that a workflow can pick a conversation back up later:

store = InMemoryACPSessionStore()

await store.put("pr-918", "reviewer", "sess_abc123")
await store.get("pr-918", "reviewer")     # -> "sess_abc123"
await store.delete("pr-918", "reviewer")

The identity is a pair, not a thread id, because one LangGraph thread routinely runs several agents — an implementer and three reviewers on the same pull request — and none of them may resume another's conversation.

A store holds ACP session identifiers and nothing else. No conversation history, no agent messages, no tool history, no model context, no agent memory: the ACP agent owns all of that and restores it itself when asked to load sess_abc123. That is what makes a reply arriving on a webhook days later resume a live conversation without reconstructing a transcript — and why the store is the piece that has to be durable. InMemoryACPSessionStore is not; it lives as long as its process, which is right for tests, examples, and single-process graphs. The durable implementations arrive with Ticket 17 and change nothing about the interface.

Two behaviours are placeholders that later tickets replace. Permission requests are streamed as acp.permission.requested and then declined, because the policy that could approve them does not exist yet; and the history an agent replays while loading a session is dropped, because no turn is streaming when it arrives.

Development

A standalone distribution, deliberately outside the uv workspace at the root of this repository: it depends on nothing OpenEngine ships, targets a newer Python than the rest of the tree, and is intended to be published on its own.

cd langgraph-acp
uv run pytest
uv run mypy    # strict, over src/ and tests/

The package ships py.typed, so its annotations are a promise to a downstream type checker; mypy --strict runs in CI on both supported interpreters to keep it. Each field is annotated with what the constructor accepts, since __post_init__ normalizes — a Path becomes a str, "reuse" becomes ACPSessionStrategy.REUSE.

The dependency list is empty and stays empty: ACP is JSON-RPC over a pipe, so the client is stdlib asyncio written here rather than a second protocol library in every application that installs this one. The client tests launch a real child process — tests/fake_agent.py, an ACP agent that does nothing, correctly — because the thing under test is a process boundary.

CI runs both only when something under langgraph-acp/ changes.

Metadata

Release files for langgraph-acp 1.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for langgraph-acp 1.7.0
File Size Uploaded
langgraph_acp-1.7.0.tar.gz 169.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for langgraph-acp 1.7.0
File Interpreter ABI Platform
langgraph_acp-1.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 221.1 kB

Release files / langgraph_acp-1.7.0.tar.gz

Download URL langgraph_acp-1.7.0.tar.gz
Size 169.1 kB
Tags Source
SHA-256 checksum
How to use checksums
ffa5496a969eab0f5be5107d191f96f2eeaa4328bc40ae6140629138a2ecb6d3
BLAKE2b-256 checksum
How to use checksums
35afb6813fdc17820455e32fc0ecf166c01b84bfa22cba3fc5135d2ae4170487
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / langgraph_acp-1.7.0-py3-none-any.whl

Download URL langgraph_acp-1.7.0-py3-none-any.whl
Size 52.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a41140cf8ad50a7831b79324a0f0cfbad5e4484ea6586f69013dbea417924a7e
BLAKE2b-256 checksum
How to use checksums
de69ecf9e720ad51744dc1f98ecc566d8838c84716eec840f6393e67979548fc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

1.7.0 This release

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page