Files
freemo 7153050211 feat(M6.1): ACP facade stubs with transport, events, and versioning
Adds cleveragents.acp package with seven modules: models (AcpMessage,
AcpEnvelope, AcpCapability, AcpPeerInfo), errors (AcpError hierarchy),
facade (AcpFacade with register/discover/send/receive), transport
(AcpTransportBase, LoopbackTransport), events (AcpEventBus),
and versioning (AcpVersion with compatible_with check).

44 Behave scenarios, 5 Robot smoke tests, ASV benchmark suite,
and reference documentation.

Ref: Day-14 Rebaseline – M6.1 ACP-facade stubs [Jeff]
2026-02-22 15:02:19 +00:00

186 lines
6.8 KiB
Markdown

# ACP (Agent Communication Protocol) Reference
## Overview
The ACP package provides the boundary layer between the CleverAgents
application core and any external orchestrator or UI. It defines a
request/response envelope, a set of named operations, and an event
streaming interface.
**Module:** `cleveragents.acp`
**ACP Version:** 1.0
## Table of Contents
- [Modes of Operation](#modes-of-operation)
- [Local Facade](#local-facade)
- [Operation Routing Table](#operation-routing-table)
- [Server Transport Stub](#server-transport-stub)
- [Event Queue](#event-queue)
- [Version Negotiation](#version-negotiation)
- [Models](#models)
- [Error Hierarchy](#error-hierarchy)
---
## Modes of Operation
| Mode | Class | Behaviour |
|--------|--------------------|--------------------------------------------------|
| Local | `AcpLocalFacade` | Routes operations to in-process service calls |
| Server | `AcpHttpTransport` | Stub — raises `AcpNotAvailableError` on all ops |
In local mode the facade translates each ACP operation into a direct
Python method call. No serialization, no network, no authentication
overhead.
---
## Local Facade
```python
from cleveragents.acp import AcpLocalFacade, AcpRequest
facade = AcpLocalFacade()
response = facade.dispatch(AcpRequest(operation="session.create"))
assert response.status == "ok"
```
### Constructor
| Parameter | Type | Default | Description |
|-----------|----------------------------|---------|-------------------------------|
| `services` | `dict[str, Any] \| None` | `None` | Named services for routing |
### Methods
| Method | Returns | Description |
|---------------------|-------------------|--------------------------------------|
| `dispatch(request)` | `AcpResponse` | Route request to handler |
| `register_service` | `None` | Register a named service |
| `list_operations` | `list[str]` | All supported operation names |
---
## Operation Routing Table
| Operation | Response Keys |
|------------------------|-----------------------------------------|
| `session.create` | `session_id`, `status` |
| `session.close` | `status` |
| `plan.create` | `plan_id`, `status` |
| `plan.execute` | `plan_id`, `status` |
| `plan.status` | `plan_id`, `phase` |
| `plan.diff` | `plan_id`, `changes` |
| `plan.apply` | `plan_id`, `status` |
| `registry.list_tools` | `tools` |
| `registry.list_resources` | `resources` |
| `context.get` | `context` |
| `event.subscribe` | `subscription_id`, `status` |
Unknown operations raise `AcpOperationNotFoundError`.
---
## Server Transport Stub
All methods on `AcpHttpTransport` raise `AcpNotAvailableError`:
| Method | Description |
|-----------------|------------------------------------------|
| `send(request)` | Would send request over HTTP |
| `connect(url)` | Would open HTTP connection |
| `disconnect()` | Would close connection |
| `is_connected()`| Returns `False` (does not raise) |
---
## Event Queue
`AcpEventQueue` provides an in-memory event queue for local mode:
| Method | Mode | Description |
|-----------------------|--------|------------------------------------|
| `publish(event)` | Local | Append event and notify callbacks |
| `subscribe_local(cb)` | Local | Register callback, return sub ID |
| `unsubscribe(id)` | Local | Remove subscription |
| `get_events(limit)` | Local | Return recent events |
| `subscribe_remote(ep)`| Server | Raises `AcpNotAvailableError` |
---
## Version Negotiation
`AcpVersionNegotiator` validates protocol version compatibility:
```python
from cleveragents.acp import AcpVersionNegotiator
negotiator = AcpVersionNegotiator()
version = negotiator.negotiate("1.0") # returns "1.0"
negotiator.negotiate("2.0") # raises AcpVersionMismatchError
```
Supported versions: `["1.0"]`
---
## Models
### AcpRequest
| Field | Type | Default |
|---------------|----------------------------|-------------------|
| `acp_version` | `str` | `"1.0"` |
| `request_id` | `str` | Auto-generated ULID |
| `operation` | `str` | *required* |
| `params` | `dict[str, Any]` | `{}` |
| `auth` | `dict[str, Any] \| None` | `None` |
### AcpResponse
| Field | Type | Default |
|---------------|------------------------------|-------------------|
| `acp_version` | `str` | `"1.0"` |
| `request_id` | `str` | *required* |
| `status` | `str` (`"ok"` or `"error"`) | *required* |
| `data` | `dict[str, Any]` | `{}` |
| `error` | `AcpErrorDetail \| None` | `None` |
| `timing_ms` | `float \| None` | `None` |
### AcpErrorDetail
| Field | Type | Default |
|-----------|-------------------|---------|
| `code` | `str` | *required* |
| `message` | `str` | *required* |
| `details` | `dict[str, Any]` | `{}` |
### AcpEvent
| Field | Type | Default |
|--------------|---------------------------|------------------------|
| `event_id` | `str` | Auto-generated ULID |
| `event_type` | `str` | *required* |
| `plan_id` | `str \| None` | `None` |
| `data` | `dict[str, Any]` | `{}` |
| `timestamp` | `str` | Auto-generated ISO UTC |
---
## Error Hierarchy
```
CleverAgentsError
└── AcpError
├── AcpNotAvailableError
├── AcpVersionMismatchError
└── AcpOperationNotFoundError
```
| Exception | When Raised |
|-----------------------------|-------------------------------------------|
| `AcpNotAvailableError` | Server-mode operation in local mode |
| `AcpVersionMismatchError` | Unsupported ACP version requested |
| `AcpOperationNotFoundError` | Unknown operation dispatched |