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.9.1

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.9.1
File Size Uploaded Provenance
langgraph_acp-1.9.1.tar.gz 169.1 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 221.1 kB

Release files / langgraph_acp-1.9.1.tar.gz

Download URL langgraph_acp-1.9.1.tar.gz
Size 169.1 kB
Tags Source
SHA-256 checksum
How to use checksums
c977a3d46a4affa207f78be71eaceceb9aa65861f0c6408d7b50bd1d9edbc426
BLAKE2b-256 checksum
How to use checksums
a56f81d4f84dbb3479feb5563441bceded68cdfe4e631e8342e655568f75d4df
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.9.1-py3-none-any.whl

Download URL langgraph_acp-1.9.1-py3-none-any.whl
Size 52.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
df9aa77b3ca72685222a22960eaf97d6a1a530fc251d4c68b1468a46b66d8bd9
BLAKE2b-256 checksum
How to use checksums
9e6f83def645725d2b2d03d5fdcd5ed0884a307547f434365c72ff26ec090ec3
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}

Security for langgraph-acp 1.9.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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