Agent Harness Protocol for Python
Agent Harness Protocol (AHP) is the business-facing port for delegating work to an agent harness. A local graph, an embedded CLI or agent server, and a remote agent service can implement the same purpose without exposing their execution loop, model calls, transport, or persistence layout to application code.
Why a port, when LangChain and LangGraph already exist?
LangChain and LangGraph are for building an agent: choosing the model, drawing the graph, wiring tools, and checkpointing state. AHP is for the application that delegates work to an agent and then has to act on the result. It does not replace a graph framework. A LangGraph graph is one of the things AHP delegates to, alongside command-line agents, agent servers, and remote agent services.
The port adds a contract written from the caller's side:
- Purpose, not mechanism. A caller states the guarantees it requires (keep no context after release, let the caller decide approvals, return output that matches a schema) instead of configuring one runtime's knobs. Capabilities are optional contracts that an adapter may add; they extend the port rather than shrinking it to what every runtime has in common.
- Refusal instead of silent downgrade. Discovery (
support), preflight (validate()), and admission are separate facts. A requirement an adapter cannot keep raisesIncompatibleRequirementError; one it cannot confirm raisesRequirementUnconfirmedError. - Honest outcomes. Cancelling a coroutine does not prove that a remote provider
stopped, so
Unresolvedstays distinct fromCancelled. Unknown usage staysNonerather than zero, and missing output differs from empty output. - Lifecycle guarantees written once. Bounded cleanup, waiters whose cancellation leaves the work running, rejection of overlapping tasks, exact loss accounting for slow observers, and one stable terminal outcome belong to the contract instead of being rebuilt in every application.
- Executable conformance. Reusable suites compare an adapter with evidence observed at its real boundary, so an application can change harnesses without rewriting the logic that depends on these guarantees.
Building the LangGraph adapter showed the gap concretely. Used directly, LangGraph leaves the following implicit; behind AHP each needs an explicit, tested answer:
- cancelling
ainvoke()does not show that the model provider stopped working; - the input of a cancelled turn remains in the checkpoint and reaches the next turn;
InMemorySavercan be configured to write to disk;- tracing and provider logs can expose inputs outside the application.
If an application designs its own graph and will only ever run LangGraph, use LangGraph directly. AHP pays off when an application delegates tasks, judges their results by explicit guarantees, and may run more than one kind of harness.
This repository is the native Python expression of the Kotlin AHP 0.2.3 contract.
The agent-harness-protocol distribution contains the protocol and reusable
conformance suites only. It deliberately ships no adapter, reference harness, or mock
implementation.
Adapters are separate distributions kept under implementations/, following the
Kotlin repository's protocol/implementations split:
| Distribution | Location | Runtime |
|---|---|---|
agent-harness-protocol |
src/ |
protocol and conformance suites, no runtime dependency |
agent-harness-protocol-langgraph |
implementations/langgraph |
LangChain BaseChatModel inside a LangGraph StateGraph |
Each adapter documents its own support matrix; an adapter's limits never change the protocol contract described here.
What the contract preserves
AgentHarnesscreates logicalAgentSessioncontexts and admits task requirements.AgentSession.start_task()returns anAgentTaskafter admission, without waiting for completion.AgentTaskexposes current state, independent event subscriptions, pending typed interactions, cancellation, and one stable terminal outcome.Completed,Failed,Cancelled, andUnresolvedremain distinct. Requesting cancellation or losing a stream never fabricates confirmed termination.- Missing output differs from
TextOutput(""); unknown usage fields differ from measured zero. - Capability discovery, preflight validation, and actual admission are separate facts. A caller may state requirements without querying support first.
The port contains no provider-payload escape hatch in inbound requests. Provider configuration belongs to an adapter's construction boundary; portable application logic deals in required purpose and guarantees.
Python API
from agent_harness_protocol import AgentHarness, Completed, SessionSpec, TaskRequest, TextInput
async def run(harness: AgentHarness) -> None:
async with harness:
session = await harness.create_session(SessionSpec(instructions="Keep changes reviewable"))
task = await session.start_task(TaskRequest(TextInput("Update the report")))
async for event in task.events():
render(event)
outcome = await task.await_outcome()
if isinstance(outcome, Completed):
consume(outcome.output)
Python's async with, AsyncIterator, snake_case, frozen dataclasses, and Error
suffixes replace Kotlin-specific resource, Flow, naming, and exception forms. The
semantic distinctions remain the same:
| Kotlin representation | Python representation |
|---|---|
Flow<TaskEvent> |
a fresh AsyncIterator[TaskEvent] from task.events() |
StateFlow<TaskState> |
immediately readable task.state snapshot |
StateFlow<List<InteractionRequest>> |
immutable task.pending_interactions snapshot |
close() / release() |
awaitable aclose() / release() and async with |
PersistenceRequirement.NotRequired |
None |
WorkspaceRequirement.NotRequired |
None |
ExecutionConstraint.ProviderDefault |
None |
Kotlin *Exception |
Python *Error |
SessionSpec.instructions=None still means provider default, while "" explicitly
requests empty instructions. None in an AgentUsage field means unknown; it is not
zero. None output means no output was acquired; it is not an empty response.
Conformance suites
Install the test extra and inherit the relevant suite from a pytest class whose name
starts with Test:
python -m pip install -e ".[test]"
from agent_harness_protocol.conformance import RuntimeProfileConformanceTests
class TestLangGraphRuntime(RuntimeProfileConformanceTests):
def boundary(self):
return langgraph_boundary_fixture()
def harness(self, boundary):
return make_langgraph_harness(boundary)
Fixture observations must come from the actual model, SDK, process, storage, or effect
boundary. They must not be copied from AgentTask.state, events, or outcomes, and a
fixture must not directly force public task state just to pass a scenario. The package
contains 58 reusable scenario methods across admission, lifecycle, interaction,
cleanup, persistence, execution bounds, workspace/skills, output, accounting, and
observation load. The requirements scenario expands every declared profile and case
in both preflight and direct-admission modes.
See the protocol mapping and
conformance guide before binding an adapter. A complete binding
against a real runtime is in
implementations/langgraph/tests/test_conformance.py.
Metadata
Release files for agent-harness-protocol 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agent_harness_protocol-0.2.3.tar.gz | 37.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agent_harness_protocol-0.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 84.6 kB
Release files / agent_harness_protocol-0.2.3.tar.gz
| Download URL | agent_harness_protocol-0.2.3.tar.gz |
|---|---|
| Size | 37.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ece5a28f2366d15f8c5e08444fd1298060f86701cf82fb6f54281c3f202c436d
|
|
BLAKE2b-256 checksum How to use checksums |
625280c7de71cdd11c2dfa0d5f34039fdf27bba6434426a95f36dc050706cddc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / agent_harness_protocol-0.2.3-py3-none-any.whl
| Download URL | agent_harness_protocol-0.2.3-py3-none-any.whl |
|---|---|
| Size | 46.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3f9c9899efdf107620e7ba56af0dd2cbf69bfc09a2d7244b341488e5f5f5b660
|
|
BLAKE2b-256 checksum How to use checksums |
64d61e29fa85d4db332990b59840229a2a92e8cc4f62d363d44294ee86d52fef
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|