Docs: Added and revised server-client protocol details

This commit is contained in:
2026-03-11 10:02:27 -04:00
parent 82def1112b
commit e98c8e6c79
13 changed files with 1570 additions and 567 deletions
+9 -3
View File
@@ -31,6 +31,12 @@ related_adrs:
- number: 26
title: Agent Client Protocol (ACP)
relationship: Defines the versioned protocol boundary between the Presentation and Application layers; all client-to-backend communication flows through ACP
- number: 47
title: ACP Standard Adoption
relationship: Adopts the external ACP standard (JSON-RPC 2.0) as the sole client-server protocol, replacing the previous REST/FastAPI approach
- number: 48
title: Server Application Architecture
relationship: Defines the server application that implements the ACP endpoint, sharing Domain and Application layers with the client
acceptance:
votes_for:
- voter: Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>
@@ -43,13 +49,13 @@ acceptance:
CleverAgents is a complex AI-powered development assistant that must support two deployment modes (local CLI and multi-user server), multiple LLM providers, swappable storage backends, and a rich domain model spanning plans, decisions, actors, tools, and resources. Without a clear structural decomposition, these concerns would become entangled — making the system difficult to test, extend, and maintain.
The architecture must enforce strict boundaries so that domain logic remains independent of infrastructure choices (which database, which LLM provider, which vector store), and so that the presentation layer (CLI, TUI, REST API) can evolve without affecting core business rules.
The architecture must enforce strict boundaries so that domain logic remains independent of infrastructure choices (which database, which LLM provider, which vector store), and so that the presentation layer (CLI, TUI, ACP Server Endpoint) can evolve without affecting core business rules.
## Decision Drivers
- Must support two deployment modes (local CLI and multi-user server) sharing the same core logic
- Domain logic must remain independent of infrastructure choices (database, LLM provider, vector store)
- Presentation surfaces (CLI, TUI, REST API) must evolve without affecting business rules
- Presentation surfaces (CLI, TUI, ACP Server Endpoint) must evolve without affecting business rules
- Need strict boundaries between layers to enable independent testing and swappable infrastructure
- System spans multiple complex subsystems (plans, decisions, actors, tools, resources) requiring clear structural decomposition
- Must support CQRS data flow and event-driven observability without entangling concerns
@@ -64,7 +70,7 @@ The strict dependency rule is: **outer layers depend on inner layers, never the
### Layer Definitions
**Presentation Layer** — The outermost layer, responsible for user interaction and external API surfaces. Contains the Typer CLI, Textual TUI, Textual Web interface, IDE plugin (embedded TUI), and REST API (FastAPI/uvicorn). All presentation components call into the Application Layer through service facades. No business logic resides here.
**Presentation Layer** — The outermost layer, responsible for user interaction and external API surfaces. Contains the Typer CLI, Textual TUI, Textual Web interface, IDE plugin (embedded TUI), and ACP Server Endpoint (JSON-RPC 2.0). All presentation components call into the Application Layer through service facades. No business logic resides here.
**Application Layer** — Orchestrates use cases by coordinating domain objects and infrastructure services. Contains:
+1 -1
View File
@@ -159,7 +159,7 @@ CleverAgents is built on **Python >= 3.13** as the sole implementation language.
| dependency-injector | >= 4.41.0 | DI container. DeclarativeContainer with Singleton, Factory, Configuration providers. |
| watchdog | >= 4.0.0 | File system monitoring. Auto re-indexing on file changes. |
| numpy | >= 2.1.0 | Vector operations for embedding similarity and confidence score aggregation. |
| uvicorn | >= 0.30.1 | ASGI server for server mode REST API (FastAPI). |
| uvicorn | >= 0.30.1 | ASGI server for hosting the ACP JSON-RPC 2.0 endpoint in server mode. |
## Constraints
+1 -1
View File
@@ -126,7 +126,7 @@ The architecture supports additional presentation surfaces without modifying the
- **Textual TUI**: Reactive terminal UI using the same Rich primitives.
- **Textual Web**: TUI served as a web application.
- **IDE Plugin**: Embedded TUI integration.
- **REST API**: FastAPI/uvicorn for server mode.
- **ACP Server**: JSON-RPC 2.0 endpoint for server mode.
All presentation surfaces consume the same Application Layer service facades.
@@ -31,6 +31,9 @@ related_adrs:
- number: 31
title: Actor Abstraction Definition
relationship: Actors are implemented as LangGraph StateGraph instances; LangGraph is the actor graph runtime
- number: 48
title: Server Application Architecture
relationship: The server deploys actor StateGraphs to LangGraph Platform and invokes them via RemoteGraph for remote plan execution
acceptance:
votes_for:
- voter: Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>
+28 -20
View File
@@ -21,14 +21,19 @@ related_adrs:
relationship: Server mode introduces server-prefixed namespaces for multi-server entity resolution
- number: 5
title: Technical Stack
relationship: FastAPI, uvicorn, and Helm/Kubernetes are the server mode stack choices
relationship: ACP Python SDK, LangGraph Platform, PostgreSQL, and Helm/Kubernetes are the server mode stack choices
- number: 19
title: Storage and Persistence
relationship: Server mode uses PostgreSQL instead of SQLite for multi-user persistence
- number: 26
title: Agent Client Protocol (ACP)
relationship: ACP is the client-server contract implemented by the REST API in server mode; its transport duality (in-process facade for local, HTTPS + JSON for server) is the mechanism that keeps both
modes behaviorally identical
relationship: ACP (external standard, JSON-RPC 2.0) is the sole client-server protocol; server mode uses ACP over HTTP, local mode uses ACP over stdio
- number: 47
title: ACP Standard Adoption
relationship: ADR-047 defines the adoption of the external ACP standard that replaces the previous REST-based protocol
- number: 48
title: Server Application Architecture
relationship: ADR-048 defines the concrete server architecture (LangGraph Platform, PostgreSQL, ACP endpoint) that implements server mode
acceptance:
votes_for:
- voter: Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>
@@ -50,7 +55,7 @@ CleverAgents starts as a single-user local CLI tool, but the specification envis
## Decision
CleverAgents supports two deployment modes that share the same Domain and Application layers. **Local mode** is a single-process CLI application with SQLite storage. **Server mode** is a multi-user service where the CLI acts as a thin client communicating with a remote server over HTTPS. The Infrastructure Layer provides swappable implementations for each mode, enabled by the hexagonal architecture (ADR-001).
CleverAgents supports two deployment modes that share the same Domain and Application layers. **Local mode** is a single-process CLI application with SQLite storage and ACP over stdio. **Server mode** is a multi-user service where the CLI acts as a thin client communicating with a remote server via **ACP over HTTP** (JSON-RPC 2.0). The Infrastructure Layer provides swappable implementations for each mode, enabled by the hexagonal architecture (ADR-001).
## Design
@@ -60,28 +65,31 @@ CleverAgents supports two deployment modes that share the same Domain and Applic
- All components run in the same Python process.
- SQLite database for persistence (WAL mode for concurrent reads).
- All resources are local to the machine.
- ACP flows over **stdio** — the agent runs as a subprocess, JSON-RPC messages over stdin/stdout.
- Extension methods (`_cleveragents/*`) resolved in-process by `AcpLocalFacade`.
- The `local/` namespace is always available.
- No server dependency — works fully offline.
### Server Mode
- The CLI operates as a thin client.
- Communication with the server over HTTPS using `server.url` and `server.token`.
- Communication with the server via **ACP over HTTP** (JSON-RPC 2.0) using `server.url` and `server.token`.
- The server hosts shared storage (PostgreSQL), namespace resolution, and remote plan execution.
- Server namespaces (`<username>/`, `<orgname>/`) are available when connected.
- Entity definitions (actors, actions, skills, tools) can be synced between local and server.
- Plans can execute on the server, with sandbox environments managed remotely.
- Entity definitions (actors, actions, skills, tools) can be synced between local and server via `_cleveragents/sync/*` extension methods.
- Plans can execute on the server, with actor graphs running on LangGraph Platform via RemoteGraph.
### Server Infrastructure
- **REST API**: FastAPI application served by uvicorn (>= 0.30.1).
- **ACP endpoint**: ACP JSON-RPC 2.0 server implemented using the ACP Python SDK's server-side connection handler. This is the sole client-facing interface.
- **Database**: PostgreSQL via SQLAlchemy (same ORM, different dialect from local SQLite).
- **Actor execution**: LangGraph Platform via RemoteGraph — each actor graph deploys as a separate RemoteGraph.
- **Deployment**: Helm chart in `k8s/` directory for Kubernetes deployment.
- **Authentication**: Token-based authentication (`server.token`).
- **Authentication**: Token-based authentication via the ACP `authenticate` method.
### Sync Mechanism
Entity synchronization between client and server:
Entity synchronization between client and server uses ACP extension methods (`_cleveragents/sync/*`):
- **Auto-sync**: Controlled by `server.sync.auto` (default: `true`). When enabled, entities are synced on startup and after registration changes.
- **Sync interval**: Background syncs at `server.sync.interval` (default: 300 seconds).
@@ -102,7 +110,7 @@ Entity synchronization between client and server:
| **Local** | Contains at least one local-only resource | Client only |
| **Remote** | All resources are remotely accessible | Client or Server |
Remote projects can have their plans executed on the server, enabling shared infrastructure and centralized execution.
Remote projects can have their plans executed on the server, enabling shared infrastructure and centralized execution. When server-hosted agents need to access client-local resources, ACP callbacks (`fs/*`, `terminal/*`) forward the requests to the connected client.
### Shared Architecture
@@ -111,17 +119,17 @@ The key architectural enabler is the hexagonal pattern (ADR-001):
- **Domain Layer**: Identical between modes. All business rules, domain models, and domain events are shared.
- **Application Layer**: Identical between modes. Service facades, workflow engine, and event bus are shared.
- **Infrastructure Layer**: Swappable implementations per mode:
- SQLite ↔ PostgreSQL (database)
- Local file access ↔ Remote API calls (resource access)
- Local sandbox ↔ Remote sandbox (execution environment)
- Direct component invocationHTTPS API calls (service communication)
- SQLite ↔ PostgreSQL (database)
- Local file access ↔ ACP callbacks (resource access)
- Local sandbox ↔ LangGraph Platform RemoteGraph (execution environment)
- AcpLocalFacade (stdio)ACP SDK server endpoint (HTTP) (transport)
## Constraints
- The `local/` namespace must never be synced to a server.
- Server mode requires `server.url` and `server.token` to be configured.
- The Domain and Application layers must have zero mode-specific code. All mode differences must be in the Infrastructure Layer.
- Token authentication is required for all server API calls.
- Token authentication is required for all server ACP method calls (enforced via the `authenticate` handshake).
- Local-only projects (containing local-only resources) cannot execute plans on the server.
- The CLI must function fully offline in local mode, with no server dependency.
@@ -134,7 +142,7 @@ The key architectural enabler is the hexagonal pattern (ADR-001):
- Teams can share entity definitions and execute plans on shared infrastructure without each member maintaining local copies.
### Negative
- Server mode requires operating a server (PostgreSQL, Kubernetes, Helm, authentication).
- Server mode requires operating a server (PostgreSQL, Kubernetes, LangGraph Platform, Helm, authentication).
- The sync mechanism adds complexity and potential for stale or conflicting entity definitions.
- Network latency affects all operations in server mode.
@@ -152,7 +160,7 @@ The key architectural enabler is the hexagonal pattern (ADR-001):
## Compliance
- **Mode isolation tests**: Tests verify that Domain and Application layer code has no mode-specific imports or branching.
- **Infrastructure swappability tests**: Tests verify that swapping SQLite for PostgreSQL, local sandbox for remote sandbox, etc., produces correct behavior.
- **Infrastructure swappability tests**: Tests verify that swapping SQLite for PostgreSQL, local sandbox for RemoteGraph, etc., produces correct behavior.
- **Offline functionality tests**: Tests verify that local mode works completely without network access.
- **Sync tests**: Integration tests verify entity sync behavior including auto-sync, manual sync, and conflict handling.
- **Authentication tests**: Tests verify that server API calls require valid tokens and that unauthenticated requests are rejected.
- **Sync tests**: Integration tests verify entity sync behavior including auto-sync, manual sync, and conflict handling via `_cleveragents/sync/*` methods.
- **Authentication tests**: Tests verify that the ACP `authenticate` flow works correctly and that unauthenticated requests are rejected.
+147 -110
View File
@@ -18,22 +18,28 @@ related_adrs:
relationship: ACP defines the fundamental boundary between the Presentation and Application layers in the four-layer architecture
- number: 6
title: Plan Lifecycle
relationship: ACP's plan operation group exposes the full plan lifecycle (use, execute, apply, cancel, correct, rollback) to all clients
relationship: ACP's plan extension methods expose the full plan lifecycle (use, execute, apply, cancel, correct, rollback) to all clients
- number: 20
title: Session Model
relationship: ACP defines the session lifecycle operations (create, tell, export, import) exposed to clients
relationship: Standard ACP session methods (session/new, session/prompt, session/load) map directly to SessionWorkflow operations
- number: 21
title: CLI and Output Rendering
relationship: Every CLI command maps 1:1 to an ACP operation; the CLI is a thin rendering layer over ACP
relationship: Every CLI command maps to an ACP method (standard or extension); the CLI is a thin rendering layer over ACP
- number: 23
title: Server Mode
relationship: ACP is the client-server contract implemented by the REST API in server mode; the transport duality (local facade vs HTTPS) is the mechanism that keeps both modes behaviorally identical
relationship: ACP is the sole client-server protocol; server mode uses ACP over HTTP, local mode uses ACP over stdio
- number: 25
title: Observability and Logging
relationship: ACP event streaming delivers structured log events and plan state changes to subscribed clients
relationship: ACP session/update notifications deliver structured plan state changes and tool call events to subscribed clients
- number: 27
title: Language Server Protocol (LSP) Integration
relationship: LSP servers are Infrastructure-layer components attached to actors; the IDE plugin (Presentation layer) communicates through ACP independently of LSP
- number: 47
title: ACP Standard Adoption
relationship: ADR-047 defines the adoption of the external ACP standard that this ADR's protocol boundary now implements
- number: 48
title: Server Application Architecture
relationship: ADR-048 defines the server that implements the ACP endpoint; this ADR defines the protocol boundary role
acceptance:
votes_for:
- voter: Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>
@@ -43,193 +49,221 @@ acceptance:
---
## Context
CleverAgents is accessed through multiple presentation-layer clients — CLI, TUI, Web, and an IDE plugin — and can operate in both local (single-process) and server (multi-user HTTPS) modes. Without a shared, versioned contract between clients and the backend, each client would require bespoke integration code that drifts from the core domain model, making interoperability fragile and third-party client development impractical. The architecture needs a single protocol surface that all clients rely on regardless of deployment mode.
CleverAgents is accessed through multiple presentation-layer clients — CLI, TUI, and an IDE plugin — and can operate in both local (single-process) and server (multi-user) modes. Without a shared, versioned contract between clients and the backend, each client would require bespoke integration code that drifts from the core domain model, making interoperability fragile and third-party client development impractical. The architecture needs a single protocol surface that all clients rely on regardless of deployment mode.
## Decision Drivers
- Multiple presentation-layer clients (CLI, TUI, Web, IDE plugin) must share a single, versioned contract to avoid bespoke integration code that drifts from the core domain model
- Local (in-process) and server (HTTPS) deployment modes must expose identical operational semantics to all clients
- Multiple presentation-layer clients (CLI, TUI, IDE plugin) must share a single, versioned contract to avoid bespoke integration code that drifts from the core domain model
- Local (in-process) and server (remote) deployment modes must expose identical operational semantics to all clients
- Third-party client development must be practical without knowledge of internal domain or infrastructure services
- Long-running operations (plan execution, tool invocations) require real-time event streaming with causal ordering guarantees
- Long-running operations (plan execution, tool invocations) require real-time streaming with causal ordering guarantees
- The protocol must support backward-compatible evolution so existing clients are not broken by new features
- Alignment with the emerging agent interoperability ecosystem is preferred over a bespoke protocol
## Decision
CleverAgents adopts the **Agent Client Protocol (ACP)** as the versioned contract governing all client-to-backend communication. ACP defines canonical operations for sessions, plans, registries, context, and event streaming. In local mode ACP maps to in-process service facade calls; in server mode it is implemented by the REST API over HTTPS.
CleverAgents adopts the **Agent Client Protocol (ACP)** the external open standard from [agentclientprotocol.org](https://agentclientprotocol.org) — as the **sole** communication protocol for all client-to-backend interaction. ACP is built on **JSON-RPC 2.0** and provides standard methods for agent conversations plus an extensibility mechanism for platform-specific operations. In local mode ACP flows over **stdio** (agent as subprocess); in server mode over **HTTP**.
## Design
### Architectural Role
ACP defines the **fundamental boundary between the Presentation and Application layers** in the CleverAgents layered architecture (ADR-001). Every arrow from a Presentation-layer component (CLI, TUI, Web, IDE plugin, REST API) to the Application-layer Service Facade represents an ACP operation. No Presentation-layer module is permitted to bypass ACP and access Domain or Infrastructure services directly.
ACP defines the **fundamental boundary between the Presentation and Application layers** in the CleverAgents layered architecture (ADR-001). Every arrow from a Presentation-layer component (CLI, TUI, IDE plugin, ACP server endpoint) to the Application-layer Service Facade represents an ACP method call. No Presentation-layer module is permitted to bypass ACP and access Domain or Infrastructure services directly.
This boundary role makes ACP the single most architecturally significant protocol in the system — MCP and Agent Skills plug into the Infrastructure and Domain layers respectively, LSP provides actor-attached language intelligence in the Infrastructure layer (see ADR-027), but ACP is the protocol surface through which *every* client operation flows.
### Protocol Scope
ACP covers five operation groups:
ACP communication is organized into two categories:
| Group | Operations | Service(s) |
|-------|-----------|------------|
| **Session lifecycle** | create, resume, list, show, delete, export, import, tell | `SessionWorkflow` |
| **Plan lifecycle** | use, execute, apply, cancel, status, tree, explain, correct, diff, artifacts, prompt, rollback | `PlanService`, `PlanLifecycle`, `CorrectionFlow` |
| **Registries** | list / show / add / update / remove for actors, skills, tools, validations, resources, resource types, projects, actions, automation profiles, invariants | `ActorService`, `ToolService`, `SkillService`, `ResourceService`, `ProjectService` |
| **Context operations** | context show, context inspect, context simulate, context set | `ContextService` |
| **Event streaming** | plan state changes, tool execution events, validation results, structured log events | `EventEmitter` (via SSE or in-process subscription) |
**Standard ACP methods** (defined by the external specification) handle the core agent conversation lifecycle:
Every CLI command maps 1:1 to an ACP operation. When a user runs `agents plan status <ID>`, the CLI constructs an ACP `plan.status` request, sends it through the active transport, and renders the response.
| Method Category | Methods | Service(s) |
|-----------------|---------|------------|
| **Connection** | `initialize`, `authenticate` | Capability negotiation, token validation |
| **Session lifecycle** | `session/new`, `session/load`, `session/list`, `session/fork`, `session/resume` | `SessionWorkflow` |
| **Conversation** | `session/prompt`, `session/cancel` | `SessionWorkflow`, `PlanService` |
| **Configuration** | `session/set_mode`, `session/set_model` | `AutomationProfileService`, `ProviderRegistry` |
### Request / Response Envelope
**CleverAgents extension methods** (`_cleveragents/`-prefixed per the ACP extensibility spec) handle platform operations:
Every ACP operation uses a canonical JSON envelope:
| Extension Group | Methods | Service(s) |
|-----------------|---------|------------|
| **Plan lifecycle** | `_cleveragents/plan/use`, `execute`, `apply`, `cancel`, `status`, `tree`, `explain`, `correct`, `diff`, `artifacts`, `prompt`, `rollback`, `list` | `PlanService`, `PlanLifecycle`, `CorrectionFlow` |
| **Registries** | `_cleveragents/registry/{entity}/list`, `show`, `add`, `update`, `remove` | `ActorService`, `ToolService`, `SkillService`, `ResourceService`, `ProjectService` |
| **Context** | `_cleveragents/context/show`, `inspect`, `simulate`, `set` | `ContextService` |
| **Sync** | `_cleveragents/sync/pull`, `push`, `status` | `SyncService` |
| **Namespace** | `_cleveragents/namespace/list`, `show`, `members` | `NamespaceService` |
| **Health** | `_cleveragents/health/check`, `_cleveragents/diagnostics/run` | Health/diagnostic services |
Every CLI command maps to an ACP method. When a user runs `agents plan status <ID>`, the CLI sends a `_cleveragents/plan/status` JSON-RPC request through the active transport and renders the response.
### Wire Format: JSON-RPC 2.0
All ACP messages use the JSON-RPC 2.0 wire format:
**Request:**
```json
{
"acp_version": "1.0",
"request_id": "<ULID>",
"operation": "plan.status",
"params": { "plan_id": "01HXRCF1..." },
"auth": { "token": "..." }
"jsonrpc": "2.0",
"id": 1,
"method": "_cleveragents/plan/status",
"params": { "plan_id": "01HXRCF1..." }
}
```
**Successful response:**
```json
{
"acp_version": "1.0",
"request_id": "<ULID>",
"status": "ok",
"data": { "plan_id": "01HXRCF1...", "phase": "execute", "state": "running" },
"error": null,
"timing": { "duration_ms": 42 }
"jsonrpc": "2.0",
"id": 1,
"result": { "plan_id": "01HXRCF1...", "phase": "execute", "state": "running" }
}
```
**Error response:**
```json
{
"acp_version": "1.0",
"request_id": "<ULID>",
"status": "error",
"data": null,
"error": { "code": "PLAN_NOT_FOUND", "message": "No plan with ID 01HXRCF1...", "details": {} }
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32001, "message": "Plan not found", "data": { "plan_id": "01HXRCF1..." } }
}
```
Error objects carry `code` (machine-readable), `message` (human-readable), and optional `details` (structured context).
### Transport Duality
ACP is **transport-agnostic** at the specification level. The system ships with two transport implementations:
**Local Mode — In-Process Calls**
```
CLI ──→ ACPLocalTransport ──→ ServiceFacade.method() ──→ Domain / Infrastructure
**Streaming notification (no `id`, no response expected):**
```json
{
"jsonrpc": "2.0",
"method": "session/update",
"params": { "session_id": "ses_01HXR...", "type": "agent_message_chunk", "data": { "content": "I'll start by..." } }
}
```
The `ACPLocalTransport` translates ACP operation names to direct Python method calls on the Service Facade. No serialization, no network, no authentication. The adapter enforces the ACP contract boundary — clients cannot call internal services directly.
### Transport Modes
**Server Mode — HTTPS + JSON**
ACP is **transport-agnostic** at the specification level. The ACP Python SDK provides two transport implementations:
**Local Mode — ACP over stdio**
```
CLI ──→ ACPHTTPTransport ──→ HTTPS POST ──→ FastAPI Router ──→ ServiceFacade.method()
CLI ──stdio──→ Agent Subprocess ──→ AcpLocalFacade ──→ ServiceFacade.method()
```
The `ACPHTTPTransport` serializes each operation into the canonical JSON request envelope, sends it to the REST API, and deserializes the JSON response envelope. The FastAPI router maps URL paths to Service Facade methods using the same operation-to-service mapping.
The client spawns the agent as a subprocess. JSON-RPC messages flow over stdin/stdout. Extension methods are intercepted by `AcpLocalFacade` and routed to in-process Application-layer services. No network, no authentication.
**Architectural enforcement of the duality:**
**Server Mode — ACP over HTTP**
- The same ACP test suite runs in both modes (contract tests).
```
CLI ──HTTP──→ CleverAgents ACP Server ──→ ServiceFacade.method()
```
The client connects to the CleverAgents server via the ACP SDK's HTTP transport. All methods (standard + extensions) flow through the single ACP JSON-RPC 2.0 endpoint. Authentication is required via the `authenticate` method.
**Architectural enforcement of transport parity:**
- The same ACP test suite runs over both transports (contract tests).
- No Presentation-layer module imports from Infrastructure (import-linter CI checks).
- The Service Facade is the sole Application-layer entry point from Presentation.
### Event Streaming
### Streaming
ACP provides real-time event channels for long-running operations:
ACP streaming uses **JSON-RPC 2.0 notifications**`session/update` messages with no `id` field. Notification types include:
- **Server mode**: Server-Sent Events (SSE) over a persistent HTTPS connection at `/api/v1/events/stream`. Clients filter by `plan_id` and/or `event_type`.
- **Local mode**: In-process subscription via RxPY observables or callback registration.
| Type | Emitted When |
|------|-------------|
| `agent_message_chunk` | Agent produces response tokens |
| `plan` | Plan state changes (entries with content, priority, status) |
| `tool_call` / `tool_call_update` | Tool execution start/progress/result |
| `mode_change` | Automation profile switched |
**Event types:**
In local mode, notifications flow as JSON-RPC messages over stdout. In server mode, the ACP SDK manages the HTTP streaming connection. Both modes deliver the same notification types with the same payload shapes.
| Event Type | Emitted When |
|------------|-------------|
| `plan.created` | Plan instantiated from action |
| `plan.phase_changed` | Phase transition (Strategize, Execute, Apply) |
| `plan.state_changed` | State change within a phase |
| `plan.decision_made` | Decision recorded in decision tree |
| `tool.invoked` / `tool.completed` | Tool execution start/finish |
| `validation.completed` | Validation finishes (pass/fail) |
| `plan.apply_completed` | Apply phase finishes |
| `plan.error` | Unrecoverable error |
| `session.message` | Orchestrator produces a message |
**Causal ordering guarantee**: Notifications within a single plan lifecycle are delivered in causal order.
**Causal ordering guarantee**: Events within a single plan lifecycle are delivered in causal order. A `plan.phase_changed` for Execute always arrives after the corresponding Strategize completion event.
### ACP Callbacks (Server → Client)
The ACP standard supports server-to-client requests for operations requiring client-side resources:
| Callback | Purpose |
|----------|---------|
| `session/request_permission` | Human-in-the-loop approval (maps to automation profile gates) |
| `fs/read_text_file` / `fs/write_text_file` | Agent accesses files on client machine |
| `terminal/*` | Agent runs commands in sandbox on client machine |
These callbacks enable server-hosted agents to interact with client-local resources without the server having direct filesystem or terminal access.
### Error Taxonomy
ACP defines structured error codes for programmatic handling:
ACP uses JSON-RPC 2.0 integer error codes:
| Error Code | HTTP Status | Meaning |
|------------|-------------|---------|
| `AUTH_REQUIRED` | 401 | Missing or expired token |
| `AUTH_FORBIDDEN` | 403 | Insufficient namespace access |
| `NOT_FOUND` | 404 | Entity does not exist |
| `ALREADY_EXISTS` | 409 | Duplicate entity |
| `INVALID_PARAMS` | 422 | Parameter validation failure |
| `INVALID_STATE` | 409 | Operation not valid in current state |
| `BUDGET_EXCEEDED` | 429 | Cost budget exceeded |
| `VERSION_MISMATCH` | 400 | Unsupported ACP version |
| `INTERNAL_ERROR` | 500 | Unexpected server failure |
| Code | Meaning | Domain Exception(s) |
|------|---------|---------------------|
| `-32700` | Parse error | — |
| `-32600` | Invalid request | — |
| `-32601` | Method not found | `AcpOperationNotFoundError` |
| `-32602` | Invalid params | `ValidationError` |
| `-32603` | Internal error | Any unhandled `Exception` |
| `-32001` | Entity not found | `ResourceNotFoundError` |
| `-32002` | Authentication required | `AuthenticationError` |
| `-32003` | Authorization forbidden | `AuthorizationError` |
| `-32004` | Invalid state | `BusinessRuleViolation` |
| `-32005` | Already exists | `DuplicateEntityError` |
| `-32006` | Budget exceeded | `BudgetExceededError` |
| `-32007` | Version mismatch | `AcpVersionMismatchError` |
| `-32008` | Plan error | `PlanError` |
### Versioning and Compatibility
- Each client advertises `acp_version` during connection.
- Servers must support the current minor version plus one prior minor version.
- Backward-compatible additions (new optional fields, new operations) are permitted within a major version; breaking changes require a major version bump.
- Multiple major versions may be served simultaneously via URL path versioning (`/api/v1/`, `/api/v2/`).
- The JSON-RPC protocol version is always `"2.0"` in the `jsonrpc` field
- ACP standard version is negotiated during `initialize` (capability exchange)
- CleverAgents extension version is advertised in `initialize` response capabilities under `_cleveragents.version`
- Servers support the current extension version plus one prior minor version
- Backward-compatible additions (new optional params, new extension methods) are permitted within a major version; breaking changes require a major version bump
### Authentication and Authorization
- **Server mode**: Token-based authentication (`server.token`) required for all ACP requests. The token maps to the `Authorization: Bearer <token>` header in HTTPS. Authorization is enforced per namespace.
- **Local mode**: Authentication is bypassed but the ACP operation surface remains identical.
- **Server mode**: Token-based authentication via the ACP `authenticate` method. The token is also sent as `Authorization: Bearer <token>` header on HTTP requests. Authorization is enforced per namespace.
- **Local mode**: Authentication is bypassed the agent subprocess runs with the user's local permissions.
### Service Facade Mapping
Each ACP operation group maps to an Application-layer service. The complete operation-to-endpoint-to-service routing is documented in the specification's [ACP Integration Architecture](../specification.md#acp-integration-architecture) section.
Each ACP method group maps to an Application-layer service. The complete method-to-service routing is documented in the specification's [ACP Integration Architecture](../specification.md#acp-integration-architecture) and [Server and Client Architecture](../specification.md#server-and-client-architecture) sections.
| ACP Group | Service |
|-----------|---------|
| Session | `SessionWorkflow` |
| Plan | `PlanService`, `PlanLifecycle`, `CorrectionFlow` |
| Registries | `ActorService`, `ToolService`, `SkillService`, `ResourceService`, `ProjectService` |
| Context | `ContextService` |
| Events | `EventEmitter` (via SSE or in-process subscription) |
| ACP Category | Service |
|-------------|---------|
| Standard session methods | `SessionWorkflow` |
| Plan extension methods | `PlanService`, `PlanLifecycle`, `CorrectionFlow` |
| Registry extension methods | `ActorService`, `ToolService`, `SkillService`, `ResourceService`, `ProjectService` |
| Context extension methods | `ContextService` |
| Sync extension methods | `SyncService` |
| Namespace extension methods | `NamespaceService` |
## Constraints
- Clients must not bypass ACP to access storage, repositories, or internal domain services directly.
- All ACP operations must be implementable in both local and server modes with identical semantics.
- All ACP operations in server mode must require valid authentication.
- ACP responses must use the canonical JSON envelope; ad-hoc response shapes are prohibited.
- Event streaming must preserve causal ordering within a single plan lifecycle.
- All ACP methods must be implementable over both stdio and HTTP transports with identical semantics.
- All ACP methods in server mode must require valid authentication (via `authenticate`).
- ACP responses must use the JSON-RPC 2.0 envelope; ad-hoc response shapes are prohibited.
- Standard ACP methods must not be modified — use `_cleveragents/` extension methods for platform-specific operations.
- Streaming notifications must preserve causal ordering within a single plan lifecycle.
## Consequences
### Positive
- Clients are interchangeable — new clients (e.g., mobile, third-party dashboards) can be built against the same stable contract.
- Local and server modes share the same operational surface, eliminating behavioral drift.
- Versioning guarantees backward compatibility for at least one prior minor version.
- Clients are interchangeable — new clients (e.g., mobile, third-party dashboards) can be built against the ACP standard.
- Local and server modes share the same protocol surface, eliminating behavioral drift.
- Alignment with the external ACP standard enables interoperability with the broader agent ecosystem.
- The ACP Python SDK provides production-quality transport implementations.
- JSON-RPC 2.0 is a mature, widely-tooled standard.
### Negative
- The protocol adds upfront design and versioning overhead.
- Some client-specific optimizations (e.g., batching, caching) are harder when constrained to a shared contract.
- Dependency on an external standard that is still evolving.
### Risks
- Protocol evolution could lag feature development if versioning discipline is not maintained.
- SSE streaming channels may become a bottleneck for plans with very high event throughput.
- The external ACP standard could change direction or lose momentum.
## Alternatives Considered
@@ -237,12 +271,15 @@ Each ACP operation group maps to an Application-layer service. The complete oper
**Direct database access for local clients** — Faster reads in local mode but breaks parity with server mode and tightly couples clients to storage schema. Rejected.
**gRPC** — Strong typing and streaming but adds a code-generation dependency and is less accessible for browser-based clients than JSON over HTTPS. May be revisited for high-throughput server-to-server communication.
**Bespoke REST API with custom JSON envelopes** — The original approach (pre-ADR-047). Functional but isolated from the ecosystem. No interoperability with third-party agents or editors. Ongoing maintenance of custom envelope format. Rejected in favor of the external ACP standard.
**gRPC** — Strong typing and streaming but adds a code-generation dependency and is less accessible for browser-based clients than JSON-RPC over HTTP. May be revisited for high-throughput server-to-server communication.
## Compliance
- **Contract tests**: Validate all ACP endpoints against the canonical schema for every supported version.
- **Local/server parity tests**: Execute the same ACP test suite in both deployment modes and assert identical results.
- **Authentication tests**: Verify token enforcement and namespace authorization in server mode.
- **Streaming tests**: Ensure event ordering and schema compliance for SSE event payloads.
- **Protocol conformance tests**: Validate all standard ACP method signatures and response schemas against the ACP specification test suite.
- **Extension method tests**: Validate all `_cleveragents/` methods against documented parameter and response schemas.
- **Transport parity tests**: Execute the same ACP test suite over stdio and HTTP transports, asserting identical results.
- **Authentication tests**: Verify the `authenticate` flow and namespace-scoped authorization in server mode.
- **Streaming tests**: Ensure notification ordering and schema compliance for `session/update` payloads.
- **Bypass detection**: Architecture tests verify that no Presentation-layer module imports from the Infrastructure layer directly.
@@ -43,13 +43,13 @@ acceptance:
---
## Context
CleverAgents is architected as a multi-frontend system with five Presentation-layer surfaces — CLI, TUI, Web, IDE Plugin, and REST API — all communicating through ACP (ADR-026). The CLI is fully specified and partially implemented. The TUI is the second Presentation-layer surface to be designed and represents a significant advancement in user experience: it enables real-time plan monitoring, multi-session management, interactive decision tree navigation, and rich conversation with actors — capabilities that are impractical in a stateless CLI.
CleverAgents is architected as a multi-frontend system with five Presentation-layer surfaces — CLI, TUI, Web, IDE Plugin, and ACP Server Endpoint — all communicating through ACP (ADR-026). The CLI is fully specified and partially implemented. The TUI is the second Presentation-layer surface to be designed and represents a significant advancement in user experience: it enables real-time plan monitoring, multi-session management, interactive decision tree navigation, and rich conversation with actors — capabilities that are impractical in a stateless CLI.
The existing Output Rendering Framework (ADR-021) was explicitly designed to support the TUI. The `OutputSession``ElementHandle``MaterializationStrategy` pipeline is format-agnostic; producer code writes to handles without knowing whether the active strategy renders to a Rich terminal, a Textual widget tree, or a JSON serializer. The TUI needs a `TuiMaterializer` that maps handle events to Textual widget operations.
The system operates on multiple codebases simultaneously. Unlike single-project tools, CleverAgents manages multiple projects and plans concurrently, with plans potentially spanning multiple projects. The TUI must surface this multi-project, multi-plan reality as a first-class concern — not as an afterthought bolted onto a single-directory file browser.
Additionally, the TUI must support real-time event streaming from the ACP event bus to provide live visibility into plan phase transitions, tool invocations, validation results, and actor reasoning — the same events available via SSE in server mode and RxPY observables in local mode.
Additionally, the TUI must support real-time event streaming from the ACP event bus to provide live visibility into plan phase transitions, tool invocations, validation results, and actor reasoning — the same events available via ACP session/update notifications in server mode and RxPY observables in local mode.
## Decision Drivers
@@ -72,7 +72,7 @@ CleverAgents adopts **Textual** (>= 1.0) as the TUI framework, with the followin
4. **TuiMaterializer integration** — A new `TuiMaterializer` implementation of `MaterializationStrategy` maps `ElementHandle` events to Textual widget operations. Each handle type maps to a specific Textual widget (see Design section). This enables all existing CLI command producers to render in the TUI without modification.
5. **ACP event subscription** — The TUI subscribes to ACP events (local mode: RxPY observables; server mode: SSE) for real-time updates to the sidebar plan/project panels, conversation stream, and notification system.
5. **ACP event subscription** — The TUI subscribes to ACP events (local mode: RxPY observables; server mode: ACP session/update notifications) for real-time updates to the sidebar plan/project panels, conversation stream, and notification system.
6. **Escape-cascading navigation** — Pressing `escape` always moves toward the main screen: closing modals, dismissing overlays, exiting fullscreen sidebar, and eventually returning to the prompt. Multiple escape presses from any state eventually reach the main chat screen.
@@ -209,7 +209,7 @@ ACPClient (local or server)
│ └──► session.message ──► Conversation: stream ActorResponse content
└── Local mode: RxPY Observable subscription
Server mode: SSE connection to /api/v1/events/stream
Server mode: ACP session/update notifications via JSON-RPC 2.0
```
### Conversation Stream Architecture
@@ -337,7 +337,7 @@ Each session tab represents an independent domain `Session` (ADR-020):
- Adding Textual as a dependency increases the installation footprint; CLI-only users who never launch the TUI still carry the dependency (mitigated by making it an optional extra: `pip install cleveragents[tui]`)
- The `TuiMaterializer` must handle all `ElementHandle` types from day one — partial implementation would cause silent failures when CLI commands run in TUI context
- Real-time event subscription creates a persistent connection (RxPY observable or SSE stream) that must be managed across session tab switches and app lifecycle
- Real-time event subscription creates a persistent connection (RxPY observable or ACP notification stream) that must be managed across session tab switches and app lifecycle
### Risks
+328
View File
@@ -0,0 +1,328 @@
---
adr_number: 47
title: "ACP Standard Adoption"
status_history:
- ["2026-03-11", "Draft", "Jeffrey Phillips Freeman"]
- ["2026-03-11", "Proposed", "Jeffrey Phillips Freeman"]
- ["2026-03-11", "Accepted", "Jeffrey Phillips Freeman"]
tier: 4
authors: ["Jeffrey Phillips Freeman"]
superseded_by:
related_adrs:
- number: 1
title: "Layered Architecture"
relationship: "ACP remains the PresentationApplication boundary; adopting the external standard changes the wire format but not the layer role"
- number: 23
title: "Server Mode"
relationship: "Server mode transport changes from REST/FastAPI to ACP-over-HTTP (JSON-RPC 2.0); shared Domain/Application layers are unaffected"
- number: 26
title: "Agent Client Protocol (ACP)"
relationship: "Supersedes the bespoke envelope and REST transport defined in ADR-026; the ACP boundary role and operation groups remain, now implemented via the external standard"
- number: 22
title: "LangChain/LangGraph Integration"
relationship: "LangGraph Platform serves as the remote actor execution backend; ACP standard handles the client-server protocol layer above it"
- number: 48
title: "Server Application Architecture"
relationship: "ADR-048 defines the server that implements the ACP standard endpoint; this ADR defines the protocol it speaks"
acceptance:
votes_for:
- voter: "Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>"
comment: "Adopting the external ACP standard eliminates our bespoke protocol, aligns with the emerging agent ecosystem, and gives us stdio+HTTP transports for free via the SDK"
votes_against: []
abstentions: []
---
## Context
ADR-026 established the Agent Client Protocol as the sole PresentationApplication boundary for all CleverAgents clients. That initial design used a bespoke JSON envelope format transported over a RESTful API (FastAPI/uvicorn, URL-path routing, HTTP verbs, SSE streaming). Since then, the **Agent Client Protocol** has emerged as an external open standard ([agentclientprotocol.org](https://agentclientprotocol.org)) built on **JSON-RPC 2.0**, with an official Python SDK (`acp` package from `agentclientprotocol/python-sdk`). The external standard provides:
- A JSON-RPC 2.0 wire format with well-defined request/response/notification schemas
- Two transports: **stdio** (subprocess) and **HTTP** (remote server)
- Standard methods for session lifecycle, prompting, streaming updates, tool calls, permission requests, file system callbacks, and terminal callbacks
- An **extensibility mechanism** (`_`-prefixed methods) for platform-specific operations
- Capability negotiation during `initialize` handshake
Adopting this external standard eliminates our bespoke protocol, provides SDK-quality transport implementations, and positions CleverAgents within the broader agent interoperability ecosystem.
## Decision Drivers
- The existing bespoke ACP protocol (custom envelope, REST routing, SSE streaming) requires ongoing maintenance and has no ecosystem adoption outside CleverAgents
- The external ACP standard covers the core agent conversation lifecycle (sessions, prompts, streaming, tool calls, permissions) which maps directly to CleverAgents SessionWorkflow and plan execution
- Platform operations (plan lifecycle, registry CRUD, entity sync, namespace management) are cleanly handled by the standard's extensibility mechanism (`_`-prefixed methods)
- The ACP Python SDK provides production-quality stdio and HTTP transports, eliminating the need for our stub `AcpHttpTransport` and custom serialization code
- IDE plugins, CLI, TUI, and external clients all benefit from speaking a standardized protocol that third parties can implement against
- JSON-RPC 2.0 is a mature, widely-adopted RPC standard with clear error code semantics and notification support
## Decision
CleverAgents adopts the **external Agent Client Protocol standard** ([agentclientprotocol.org](https://agentclientprotocol.org)) as the **sole** communication protocol for all client-server interaction. This replaces the bespoke REST+JSON envelope protocol defined in ADR-026. The protocol role of ACP — as the PresentationApplication boundary — is unchanged; only the wire format, transport, and method vocabulary change.
**No REST API.** All communication uses ACP (JSON-RPC 2.0). Standard ACP methods handle agent conversations. ACP extension methods (`_`-prefixed) handle all platform operations.
## Design
### Protocol Architecture
ACP is the **only** protocol between any client and the CleverAgents backend. There are two categories of methods:
**Standard ACP methods** (defined by the external specification):
| Method | Direction | Purpose |
|--------|-----------|---------|
| `initialize` | Client → Server | Capability negotiation, version exchange |
| `authenticate` | Client → Server | Token-based authentication |
| `session/new` | Client → Server | Create a new conversation session |
| `session/load` | Client → Server | Resume an existing session |
| `session/list` | Client → Server | List available sessions |
| `session/prompt` | Client → Server | Send user message to agent (maps to `SessionWorkflow.tell()`) |
| `session/cancel` | Client → Server | Cancel in-progress operation |
| `session/set_mode` | Client → Server | Switch automation profile |
| `session/set_model` | Client → Server | Switch LLM provider/model |
| `session/fork` | Client → Server | Fork a session |
| `session/resume` | Client → Server | Resume a paused session |
**Standard ACP notifications** (server → client streaming):
| Notification | Type | Purpose |
|-------------|------|---------|
| `session/update` | `agent_message_chunk` | Streaming agent response tokens |
| `session/update` | `user_message_chunk` | Echo of user input |
| `session/update` | `plan` | Plan state with entries (maps to CleverAgents plan concept) |
| `session/update` | `tool_call` | Tool invocation started |
| `session/update` | `tool_call_update` | Tool invocation progress/result |
| `session/update` | `mode_change` | Automation profile changed |
**Standard ACP callbacks** (server → client requests):
| Callback | Direction | Purpose |
|----------|-----------|---------|
| `session/request_permission` | Server → Client | Human-in-the-loop approval (maps to automation profile gates) |
| `fs/read_text_file` | Server → Client | Agent reads file on client machine |
| `fs/write_text_file` | Server → Client | Agent writes file on client machine |
| `terminal/create` | Server → Client | Agent requests terminal on client |
| `terminal/output` | Server → Client | Terminal output streaming |
| `terminal/release` | Server → Client | Release terminal |
| `terminal/wait_for_exit` | Server → Client | Wait for terminal process to exit |
| `terminal/kill` | Server → Client | Kill terminal process |
**CleverAgents extension methods** (`_`-prefixed per ACP extensibility spec):
All platform operations use the `_cleveragents/` namespace. See the specification's [Server & Client Architecture](#) section for the complete method catalog with parameters and response schemas.
### Mapping to CleverAgents Concepts
| ACP Standard Concept | CleverAgents Concept |
|---------------------|---------------------|
| `session/prompt` | `SessionWorkflow.tell()` — user talks to orchestrator actor |
| `session/update` (plan) | Plan with entries (content, priority, status) — direct mapping |
| `session/update` (tool_call) | Tool invocations during plan execution |
| `session/request_permission` | Automation profile human-in-the-loop approval gates |
| `fs/*` callbacks | Agent accesses client-local project resources |
| `terminal/*` callbacks | Agent runs commands in sandbox on client machine |
| `session/set_mode` | Automation profile switching (manual → supervised → autonomous) |
| `session/set_model` | LLM provider/model switching |
| `initialize` capabilities | Advertise supported extensions, automation profiles, tool registries |
### Transport Modes
The system supports three transport configurations:
**Local mode — ACP over stdio:**
```
Client Process ──stdio──→ Agent Subprocess (ACP server)
├── AcpLocalFacade (platform ops resolved in-process)
└── Actor Graph (LangGraph, local execution)
```
The client spawns the agent as a subprocess. ACP messages flow over stdin/stdout using the ACP SDK's stdio transport. Platform operations (registry, plan lifecycle, etc.) are handled by `AcpLocalFacade` which routes to in-process Application-layer services. No network, no serialization beyond JSON-RPC framing.
**Server mode — ACP over HTTP (CleverAgents server):**
```
Client ──HTTP──→ CleverAgents ACP Server
├── Standard ACP methods → SessionWorkflow, Actor Graphs (via LangGraph Platform RemoteGraph)
└── Extension methods → PlanService, RegistryServices, SyncService, NamespaceService
```
The client connects to the CleverAgents server via the ACP SDK's HTTP transport. All communication — both agent conversations and platform operations — flows through the single ACP endpoint. The server delegates actor execution to LangGraph Platform via RemoteGraph.
**Server mode — External ACP agent + CleverAgents server:**
```
Client ──HTTP──→ External ACP Server (agent conversations)
──HTTP──→ CleverAgents ACP Server (extension methods only)
```
When an agent is hosted on an external ACP-compatible server, the client sends standard ACP methods to that server and `_cleveragents/` extension methods to the CleverAgents server. This enables interoperability with any ACP-compliant agent.
### Wire Format: JSON-RPC 2.0
All ACP messages use JSON-RPC 2.0 framing:
**Request:**
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "session/prompt",
"params": {
"session_id": "ses_01HXRCF1...",
"message": "Refactor the auth module to use dependency injection"
}
}
```
**Response:**
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"status": "accepted"
}
}
```
**Notification (no `id`, no response expected):**
```json
{
"jsonrpc": "2.0",
"method": "session/update",
"params": {
"session_id": "ses_01HXRCF1...",
"type": "agent_message_chunk",
"data": {
"content": "I'll start by extracting the authentication..."
}
}
}
```
**Error response:**
```json
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "Plan not found",
"data": { "plan_id": "01HXRCF1..." }
}
}
```
**Extension method request:**
```json
{
"jsonrpc": "2.0",
"id": 42,
"method": "_cleveragents/plan/status",
"params": { "plan_id": "01HXRCF1..." }
}
```
### Error Code Mapping
ACP uses JSON-RPC 2.0 integer error codes:
| Code | Meaning | Domain Exception(s) |
|------|---------|---------------------|
| `-32700` | Parse error (malformed JSON) | — |
| `-32600` | Invalid request | — |
| `-32601` | Method not found | `AcpOperationNotFoundError` |
| `-32602` | Invalid params | `ValidationError` |
| `-32603` | Internal error | Any unhandled `Exception` |
| `-32001` | Entity not found | `ResourceNotFoundError` |
| `-32002` | Authentication required | `AuthenticationError` |
| `-32003` | Authorization forbidden | `AuthorizationError` |
| `-32004` | Invalid state | `BusinessRuleViolation` |
| `-32005` | Already exists | `DuplicateEntityError` |
| `-32006` | Budget exceeded | `BudgetExceededError` |
| `-32007` | Version mismatch | `AcpVersionMismatchError` |
| `-32008` | Plan error | `PlanError` |
Application-specific error codes use the range `-32001` to `-32099` per JSON-RPC 2.0 convention.
### Authentication
Authentication uses the ACP standard `authenticate` method during connection setup:
1. Client calls `initialize` with capabilities (including supported extensions)
2. Server responds with its capabilities
3. Client calls `authenticate` with `{ "token": "<server.token>" }`
4. Server validates and returns authentication status
For HTTP transport, the token is also sent as `Authorization: Bearer <token>` header on every request (standard HTTP auth, compatible with JSON-RPC over HTTP).
Local mode (stdio transport) bypasses authentication entirely — the agent subprocess runs with the user's local permissions.
### Versioning
- The JSON-RPC protocol version is always `"2.0"` in the `jsonrpc` field
- ACP standard version is negotiated during `initialize` (capability exchange)
- CleverAgents extension version is advertised in the `initialize` response capabilities under `_cleveragents.version`
- Backward compatibility: servers support current extension version plus one prior minor version
- Breaking changes to extension methods require a major version bump of the `_cleveragents` extension namespace
### Client-Side Architecture Evolution
The existing ACP source code evolves as follows:
| Current | Becomes | Notes |
|---------|---------|-------|
| `AcpLocalFacade` | **Retained** | Adapts interface to ACP method signatures; handles extension method dispatch to in-process services |
| `AcpHttpTransport` (stub) | **Replaced** by ACP SDK `ClientSideConnection` | The SDK provides production HTTP transport |
| `AcpRequest` / `AcpResponse` | **Deprecated** | Replaced by ACP SDK native JSON-RPC types |
| `AcpEvent` | **Mapped** to `session/update` notifications | Event types map to ACP notification `type` field |
| `AcpEventQueue` | **Retained** for local mode | Delivers events as in-process ACP notifications |
| `AcpVersionNegotiator` | **Replaced** by `initialize` handshake | Capability negotiation per ACP standard |
| `ServerClient` / `AuthClient` / `RemoteExecutionClient` protocols | **Implemented** via ACP SDK | Single `AcpClient` wraps the SDK connection |
## Constraints
- All client-server communication **must** use ACP. No REST endpoints, no ad-hoc HTTP calls, no separate API surfaces.
- Standard ACP methods must not be modified or extended — use `_cleveragents/` extension methods for platform-specific operations.
- Extension method names must follow the `_cleveragents/{domain}/{operation}` naming convention.
- The `initialize` handshake must complete before any other method call.
- Clients must gracefully handle servers that do not advertise `_cleveragents` capabilities (indicating a non-CleverAgents ACP server).
## Consequences
### Positive
- Eliminates the bespoke REST protocol, reducing maintenance burden and protocol-specific code
- Aligns with the emerging agent interoperability ecosystem — any ACP-compatible client or agent can interact with CleverAgents
- The ACP Python SDK provides production-quality stdio and HTTP transports with proper connection lifecycle management
- JSON-RPC 2.0 is a mature, well-tooled standard with broad language support for potential non-Python clients
- IDE plugins benefit from a standard protocol that editor developers may already support
- The extensibility mechanism cleanly separates standard agent operations from platform-specific operations
### Negative
- Migrating from the bespoke protocol requires updating all existing ACP source code and tests
- JSON-RPC 2.0's error code scheme (integers) is less self-documenting than the previous string-based codes (e.g., `NOT_FOUND` vs `-32001`)
- The ACP standard is still evolving; breaking changes in future versions could require adaptation
### Risks
- The external ACP standard could change direction or lose momentum, leaving CleverAgents dependent on an abandoned standard. Mitigation: the protocol is simple enough to maintain a fork if needed.
- The extension method namespace (`_cleveragents/`) could collide with other implementations. Mitigation: the `_cleveragents` prefix is unique and registered.
## Alternatives Considered
**Keep the bespoke REST protocol (ADR-026 as-is)** — Functional but isolated. No ecosystem interoperability, ongoing maintenance of custom envelope format, and no path to IDE-native agent protocol support. Rejected.
**Adopt ACP standard for agent conversations only, keep REST for platform operations** — Simpler migration but creates two protocol surfaces, complicating client implementation and authentication. The ACP extensibility mechanism makes this unnecessary. Rejected.
**gRPC for platform operations** — Strong typing and streaming but adds code generation dependency, a second protocol surface, and is harder to debug. The ACP extension methods over JSON-RPC 2.0 are sufficient. Rejected.
## Compliance
- **Protocol conformance tests**: Validate all standard ACP method signatures and response schemas against the ACP specification test suite.
- **Extension method tests**: Validate all `_cleveragents/` methods against documented parameter and response schemas.
- **Transport parity tests**: Execute the same test suite over stdio transport (local mode) and HTTP transport (server mode), asserting identical results.
- **SDK integration tests**: Verify ACP Python SDK `ClientSideConnection` works correctly for both transport modes.
- **Capability negotiation tests**: Verify `initialize` handshake correctly advertises and consumes capabilities.
- **Bypass detection**: Architecture tests verify no Presentation-layer module bypasses ACP to access Domain or Infrastructure services.
@@ -0,0 +1,229 @@
---
adr_number: 48
title: "Server Application Architecture"
status_history:
- ["2026-03-11", "Draft", "Jeffrey Phillips Freeman"]
- ["2026-03-11", "Proposed", "Jeffrey Phillips Freeman"]
- ["2026-03-11", "Accepted", "Jeffrey Phillips Freeman"]
tier: 4
authors: ["Jeffrey Phillips Freeman"]
superseded_by:
related_adrs:
- number: 1
title: "Layered Architecture"
relationship: "The server shares the same Domain and Application layers as the client; only Infrastructure and Presentation layers differ"
- number: 22
title: "LangChain/LangGraph Integration"
relationship: "The server deploys actor StateGraphs to LangGraph Platform and invokes them via RemoteGraph"
- number: 23
title: "Server Mode"
relationship: "Refines ADR-023's server mode design with concrete architecture — LangGraph Platform for execution, ACP standard for protocol, PostgreSQL for persistence"
- number: 26
title: "Agent Client Protocol (ACP)"
relationship: "The server implements the ACP standard endpoint; ADR-026 defines the protocol boundary role"
- number: 47
title: "ACP Standard Adoption"
relationship: "ADR-047 defines the protocol the server speaks (ACP standard + extensions); this ADR defines the server that implements it"
acceptance:
votes_for:
- voter: "Jeffrey Phillips Freeman <Jeffrey.Freeman@CleverThis.com>"
comment: "A separate server application sharing domain/application layers with the client, backed by LangGraph Platform for remote execution, provides clean separation of concerns while maximizing code reuse"
votes_against: []
abstentions: []
---
## Context
ADR-023 established that CleverAgents supports local and server deployment modes sharing Domain and Application layers, differing only in Infrastructure and Presentation. ADR-047 adopted the external ACP standard as the sole client-server protocol. What remains unspecified is the concrete architecture of the server application itself — how it is structured, how it executes actor graphs remotely, how it manages multi-user state, and how it integrates with the shared layers.
## Decision Drivers
- The server must share the same Domain and Application layers as the client to avoid behavioral drift (ADR-001, ADR-023)
- Remote actor graph execution must not limit plan capabilities — different actors for different plan phases (strategy, execution) must each deploy independently
- The server must speak the ACP standard protocol (ADR-047) as its sole client-facing interface
- Multi-user persistence requires PostgreSQL (not SQLite) with proper isolation and namespace-scoped authorization
- The server must be deployable to Kubernetes via Helm charts for production environments
- LangGraph Platform is the established graph execution engine (ADR-022) and provides RemoteGraph for remote invocation
## Decision
The CleverAgents server is a **separate application** (distinct repository/deployment unit) that shares the same Domain and Application layers as the client. It implements the ACP standard endpoint (JSON-RPC 2.0 over HTTP) for all client communication, delegates actor graph execution to **LangGraph Platform** via **RemoteGraph**, and uses PostgreSQL for multi-user persistence.
## Design
### Server Application Structure
The server is organized following the same four-layer architecture as the client (ADR-001), sharing two layers and providing its own implementations for the other two:
| Layer | Client | Server | Shared? |
|-------|--------|--------|---------|
| **Domain** | Business rules, domain models, domain events | Same | Yes — identical package |
| **Application** | Service facades, workflows, event bus | Same | Yes — identical package |
| **Infrastructure** | SQLite, local sandbox, local file access, `AcpLocalFacade` | PostgreSQL, LangGraph Platform RemoteGraph, server-side sandbox, ACP SDK server | No — different implementations |
| **Presentation** | CLI (Typer), TUI (Textual), IDE plugin | ACP JSON-RPC 2.0 endpoint | No — different entry points |
The shared Domain and Application layers are consumed as a Python package dependency. This ensures zero behavioral drift between local and server modes.
### Presentation Layer: ACP Endpoint
The server's Presentation layer is an **ACP JSON-RPC 2.0 endpoint** implemented using the ACP Python SDK's server-side connection handler. This is the **only** client-facing interface — there is no REST API, no GraphQL, no separate admin endpoint.
The endpoint handles:
- **Standard ACP methods**: `initialize`, `authenticate`, `session/*` — routed to `SessionWorkflow` and actor execution
- **Extension methods**: `_cleveragents/*` — routed to Application-layer services (PlanService, RegistryServices, SyncService, etc.)
- **Callbacks to clients**: `session/request_permission`, `fs/*`, `terminal/*` — forwarded from executing actors back to the connected client
### Infrastructure Layer: LangGraph Platform
Actor graphs (StateGraphs defined in YAML) are **deployed to LangGraph Platform** as separate deployments. The server invokes them via **RemoteGraph**:
```
Client ──ACP──→ Server ACP Endpoint
├── SessionWorkflow (Application Layer)
│ │
│ └── RemoteGraph.invoke(actor_graph, state)
│ │
│ └── LangGraph Platform (remote actor execution)
│ ├── Strategy Actor Graph
│ ├── Execution Actor Graph
│ └── Estimation Actor Graph
└── Extension Method Handlers
├── PlanService
├── RegistryServices
├── SyncService
└── NamespaceService
```
This does **not** limit plan execution. Different actors for different plan phases (strategy vs. execution vs. estimation) each deploy as **separate RemoteGraphs**. The `SessionWorkflow` orchestrates them the same way it orchestrates local actor graphs, but through the RemoteGraph interface instead of direct in-process invocation.
### Infrastructure Layer: Persistence
| Component | Technology | Notes |
|-----------|-----------|-------|
| Database | PostgreSQL via SQLAlchemy | Same ORM as client (SQLite), different dialect |
| Migrations | Alembic | Schema migrations for server-specific tables (users, tokens, namespace ACLs) |
| Session store | PostgreSQL | Multi-user session persistence with namespace isolation |
| Plan store | PostgreSQL | Plan lifecycle records, decision trees, artifacts |
The SQLAlchemy repository implementations are shared between client and server where the schema is identical. Server-specific tables (authentication, namespace ACLs, user management) are additive.
### Infrastructure Layer: Additional Components
| Component | Technology | Purpose |
|-----------|-----------|---------|
| Logging | structlog | Structured logging consistent with client (ADR-025) |
| Caching | Redis (optional) | Multi-instance session affinity, rate limiting. Single-instance deployments can use in-memory caching. |
| Task queue | None (synchronous) | Plans execute synchronously via RemoteGraph. No background job queue — the ACP connection stays open during execution with streaming notifications. |
### Security Architecture
**Authentication flow:**
1. Client connects via HTTP and sends `initialize` request
2. Server responds with capabilities (including `_cleveragents` extensions)
3. Client sends `authenticate` with `{ "token": "<server.token>" }`
4. Server validates token against its user/token store
5. All subsequent requests on this connection are authenticated
**Authorization:**
- Namespace-scoped: users can only access entities in namespaces they have been granted access to
- The `local/` namespace is never accessible via server — it exists only on the client
- Server namespaces (`<username>/`, `<orgname>/`) have explicit ACLs
**Transport security:**
- HTTPS required for all production deployments (TLS termination at ingress)
- The ACP SDK HTTP transport supports standard TLS
### Deployment Architecture
| Component | Technology | Notes |
|-----------|-----------|-------|
| Container | Docker | Server packaged as container image |
| Orchestration | Kubernetes | Production deployment target |
| Configuration | Helm chart (`k8s/`) | Configurable replicas, resource limits, ingress |
| Ingress | Kubernetes Ingress | TLS termination, routing |
| Database | PostgreSQL (managed or self-hosted) | External to the application container |
| LangGraph Platform | Managed or self-hosted | External service for graph execution |
### Sync Protocol
Entity synchronization between client and server uses ACP extension methods:
| Method | Purpose |
|--------|---------|
| `_cleveragents/sync/pull` | Download server namespace entities to local cache |
| `_cleveragents/sync/push` | Upload local entity definitions to a server namespace |
| `_cleveragents/sync/status` | Compare local and server entity versions |
Sync behavior:
- **Auto-sync** (`server.sync.auto`): Entities sync on connection and at `server.sync.interval` (default: 300s)
- **Direction**: Server → local for server namespaces; local → server only on explicit push
- **Conflict resolution**: Server version wins by default; conflicts are surfaced to the user via `sync/status`
- The `local/` namespace is **never** synced
### Technology Stack
| Component | Version | Purpose |
|-----------|---------|---------|
| Python | >= 3.13 | Runtime |
| ACP Python SDK | Latest | ACP JSON-RPC 2.0 server implementation |
| LangGraph Platform | Latest | Remote actor graph execution |
| PostgreSQL | >= 15 | Multi-user persistence |
| SQLAlchemy | >= 2.0 | ORM (shared with client) |
| Alembic | >= 1.13 | Database migrations |
| structlog | >= 24.1 | Structured logging |
| Redis | >= 7.0 (optional) | Caching, session affinity for multi-instance |
| Helm | >= 3.0 | Kubernetes deployment |
## Constraints
- The server **must not** contain any Domain or Application layer code that is not shared with the client. All mode differences must be in Infrastructure and Presentation layers.
- The server's **only** client-facing interface is the ACP JSON-RPC 2.0 endpoint. No REST API, no GraphQL, no admin endpoints.
- Actor graph execution **must** go through LangGraph Platform RemoteGraph — the server does not execute actor graphs in-process.
- The `local/` namespace must never be accessible or syncable via the server.
- All ACP connections require authentication (via `authenticate` method) before any non-`initialize` operation.
## Consequences
### Positive
- Maximum code reuse — Domain and Application layers are identical, eliminating behavioral drift between local and server modes
- LangGraph Platform provides production-grade graph execution with built-in scaling, persistence, and monitoring
- The ACP-only interface ensures all clients (CLI, TUI, IDE, third-party) interact identically with the server
- RemoteGraph per actor enables independent scaling of different actor types (strategy actors may need different resources than execution actors)
- PostgreSQL provides proven multi-user persistence with proper transaction isolation
### Negative
- Dependency on LangGraph Platform — the server cannot execute actors without it
- Operating a server requires PostgreSQL, Kubernetes, and optionally Redis — significantly more infrastructure than local mode
- The ACP Python SDK is relatively new; production edge cases may require SDK contributions or workarounds
### Risks
- LangGraph Platform availability directly affects server plan execution. Mitigation: health checks and graceful degradation (report unavailability, don't silently fail)
- Network latency between server and LangGraph Platform adds overhead to every actor invocation. Mitigation: co-locate server and LangGraph Platform in the same region/cluster
- The shared Domain/Application package must be versioned carefully to avoid incompatibilities between client and server releases. Mitigation: semantic versioning with backward compatibility guarantees
## Alternatives Considered
**Single monolithic server (no LangGraph Platform)** — Execute actor graphs in the server process directly. Simpler deployment but loses LangGraph Platform's scaling, persistence, and monitoring. Doesn't align with ADR-022's adoption of LangGraph. Rejected.
**Server in the same repository as the client** — Simpler development workflow but conflates deployment units and makes it harder to version the shared layers independently. A separate application with shared package dependencies is cleaner. Rejected.
**Multiple API surfaces (ACP + REST admin)** — An admin REST API alongside ACP for server management. Adds a second protocol surface, complicates authentication, and violates the single-protocol principle. Admin operations are handled via `_cleveragents/` extension methods. Rejected.
## Compliance
- **Shared layer tests**: Tests verify that Domain and Application layers imported by the server are byte-identical to those used by the client (same package version).
- **ACP conformance tests**: The server passes the ACP standard test suite for all standard methods.
- **Extension method tests**: All `_cleveragents/` methods are tested against documented parameter and response schemas.
- **RemoteGraph integration tests**: Tests verify actor graph deployment to and invocation via LangGraph Platform.
- **Authentication tests**: Tests verify the `authenticate` flow and namespace-scoped authorization.
- **Sync tests**: Integration tests verify pull/push/status sync operations including conflict detection.
- **Deployment tests**: Helm chart renders correctly and the container starts with valid configuration.
+2
View File
@@ -177,6 +177,8 @@ nav[("scoped_backend_view",)] = "scoped_backend_view.md"
nav[("acms_fusion",)] = "acms_fusion.md"
nav[("context_indexing",)] = "context_indexing.md"
nav[("resource_type_inheritance",)] = "resource_type_inheritance.md"
nav[("temporal_data_model",)] = "temporal_data_model.md"
nav[("uko_indexer",)] = "uko_indexer.md"
# Write the literate-nav summary file for the Reference section
with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
+258 -190
View File
@@ -1,51 +1,205 @@
# ACP (Agent Communication Protocol) Reference
# ACP (Agent Client 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.
CleverAgents adopts the external **Agent Client Protocol** standard
([agentclientprotocol.org](https://agentclientprotocol.org)) as the sole
communication protocol for all client-server interaction. ACP is built on
**JSON-RPC 2.0** and provides the boundary layer between the Presentation
and Application layers.
**Module:** `cleveragents.acp`
**ACP Version:** 1.0
**Module:** `cleveragents.acp`
**Protocol:** JSON-RPC 2.0 (per ACP standard)
**SDK:** `acp` package ([agentclientprotocol/python-sdk](https://github.com/agentclientprotocol/python-sdk))
## Table of Contents
- [Modes of Operation](#modes-of-operation)
- [Transport Modes](#transport-modes)
- [Standard ACP Methods](#standard-acp-methods)
- [Extension Methods](#extension-methods)
- [Streaming Notifications](#streaming-notifications)
- [ACP Callbacks](#acp-callbacks)
- [Local Facade](#local-facade)
- [Service Wiring](#service-wiring)
- [Operation Routing Table](#operation-routing-table)
- [Error Code Taxonomy](#error-code-taxonomy)
- [Server Transport Stub](#server-transport-stub)
- [Event Queue](#event-queue)
- [Version Negotiation](#version-negotiation)
- [Models](#models)
- [Authentication](#authentication)
- [Client Architecture](#client-architecture)
- [Error Hierarchy](#error-hierarchy)
---
## Modes of Operation
## Transport Modes
| Mode | Class | Behaviour |
|--------|--------------------|--------------------------------------------------|
| Local | `AcpLocalFacade` | Routes operations to in-process service calls |
| Server | `AcpHttpTransport` | Stub — raises `AcpNotAvailableError` on all ops |
| Mode | Transport | Class / SDK Component | Behaviour |
|--------|--------------------|--------------------------------|--------------------------------------------------------|
| Local | ACP over stdio | `AcpLocalFacade` + ACP SDK stdio transport | Agent as subprocess; extensions resolved in-process |
| Server | ACP over HTTP | ACP SDK `ClientSideConnection` over HTTP | JSON-RPC 2.0 to CleverAgents server |
In local mode the facade translates each ACP operation into a direct
Python method call. No serialization, no network, no authentication
overhead.
In local mode the agent runs as a subprocess. JSON-RPC messages flow over
stdin/stdout. Extension methods (`_cleveragents/*`) are intercepted by
`AcpLocalFacade` and routed to in-process Application-layer services.
No network, no authentication.
In server mode the client connects via the ACP SDK's HTTP transport.
All methods — standard ACP and extensions — flow through the single
ACP JSON-RPC 2.0 endpoint on the server.
---
## Standard ACP Methods
These methods are defined by the external ACP specification and handle
the core agent conversation lifecycle:
| Method | Direction | Purpose |
|--------|-----------|---------|
| `initialize` | Client → Server | Capability negotiation, version exchange |
| `authenticate` | Client → Server | Token-based authentication |
| `session/new` | Client → Server | Create a new conversation session |
| `session/load` | Client → Server | Resume an existing session |
| `session/list` | Client → Server | List available sessions |
| `session/prompt` | Client → Server | Send user message to agent |
| `session/cancel` | Client → Server | Cancel in-progress operation |
| `session/set_mode` | Client → Server | Switch automation profile |
| `session/set_model` | Client → Server | Switch LLM provider/model |
| `session/fork` | Client → Server | Fork a session |
| `session/resume` | Client → Server | Resume a paused session |
---
## Extension Methods
Platform operations use `_cleveragents/`-prefixed extension methods
(per the ACP extensibility specification):
### Plan Operations
| Method | Service Method | Required Params |
|--------|---------------|-----------------|
| `_cleveragents/plan/use` | `PlanService.create_plan()` | `action_name`, `project_names` |
| `_cleveragents/plan/execute` | `PlanLifecycle.execute()` | `plan_id` |
| `_cleveragents/plan/apply` | `PlanLifecycle.apply()` | `plan_id` |
| `_cleveragents/plan/cancel` | `PlanService.cancel()` | `plan_id` |
| `_cleveragents/plan/status` | `PlanService.get_status()` | `plan_id` |
| `_cleveragents/plan/tree` | `PlanService.get_tree()` | `plan_id` |
| `_cleveragents/plan/explain` | `PlanService.explain_decision()` | `plan_id`, `decision_id` |
| `_cleveragents/plan/correct` | `CorrectionFlow.correct()` | `plan_id`, `decision_id`, `correction` |
| `_cleveragents/plan/diff` | `PlanService.get_diff()` | `plan_id` |
| `_cleveragents/plan/artifacts` | `PlanService.get_artifacts()` | `plan_id` |
| `_cleveragents/plan/prompt` | `PlanService.inject_guidance()` | `plan_id`, `message` |
| `_cleveragents/plan/rollback` | `PlanLifecycle.rollback()` | `plan_id` |
| `_cleveragents/plan/list` | `PlanService.list()` | *(optional filters)* |
### Registry Operations
Pattern applies to all entity types (`actor`, `skill`, `tool`, `validation`,
`resource`, `resource_type`, `project`, `action`, `automation_profile`,
`invariant`, `lsp`):
| Method Pattern | Service Method Pattern | Required Params |
|---------------|----------------------|-----------------|
| `_cleveragents/registry/{entity}/list` | `{Entity}Service.list()` | `namespace` (optional) |
| `_cleveragents/registry/{entity}/show` | `{Entity}Service.show()` | `name` |
| `_cleveragents/registry/{entity}/add` | `{Entity}Service.add()` | Entity-specific fields |
| `_cleveragents/registry/{entity}/update` | `{Entity}Service.update()` | `name`, updated fields |
| `_cleveragents/registry/{entity}/remove` | `{Entity}Service.remove()` | `name` |
### Context Operations
| Method | Service Method | Required Params |
|--------|---------------|-----------------|
| `_cleveragents/context/show` | `ContextService.show()` | `project_name` |
| `_cleveragents/context/inspect` | `ContextService.inspect()` | `project_name` |
| `_cleveragents/context/simulate` | `ContextService.simulate()` | `project_name`, simulation params |
| `_cleveragents/context/set` | `ContextService.set()` | `project_name`, context data |
### Sync Operations
| Method | Service Method | Required Params |
|--------|---------------|-----------------|
| `_cleveragents/sync/pull` | `SyncService.pull()` | `namespace` |
| `_cleveragents/sync/push` | `SyncService.push()` | `namespace`, `entities` |
| `_cleveragents/sync/status` | `SyncService.status()` | `namespace` (optional) |
### Namespace Operations
| Method | Service Method | Required Params |
|--------|---------------|-----------------|
| `_cleveragents/namespace/list` | `NamespaceService.list()` | — |
| `_cleveragents/namespace/show` | `NamespaceService.show()` | `namespace` |
| `_cleveragents/namespace/members` | `NamespaceService.members()` | `namespace` |
### Health and Diagnostics
| Method | Purpose |
|--------|---------|
| `_cleveragents/health/check` | Server health check |
| `_cleveragents/diagnostics/run` | Run diagnostic suite |
---
## Streaming Notifications
ACP streaming uses JSON-RPC 2.0 notifications (`session/update`) — messages
with no `id` field that expect no response:
| Notification Type | Emitted When |
|-------------------|-------------|
| `agent_message_chunk` | Agent produces response tokens |
| `plan` | Plan state changes (entries with content, priority, status) |
| `tool_call` | Tool execution begins |
| `tool_call_update` | Tool execution progress/result |
| `mode_change` | Automation profile switched |
### Example Notification
```json
{
"jsonrpc": "2.0",
"method": "session/update",
"params": {
"session_id": "ses_01HXR...",
"type": "tool_call",
"data": { "tool_name": "local/git-diff", "arguments": { "resource": "local/platform-repo" } }
}
}
```
---
## ACP Callbacks
Server-to-client requests for operations requiring client-side resources:
| Callback | Direction | Purpose |
|----------|-----------|---------|
| `session/request_permission` | Server → Client | Human-in-the-loop approval |
| `fs/read_text_file` | Server → Client | Agent reads file on client machine |
| `fs/write_text_file` | Server → Client | Agent writes file on client machine |
| `terminal/create` | Server → Client | Agent requests terminal on client |
| `terminal/output` | Server → Client | Terminal output streaming |
| `terminal/release` | Server → Client | Release terminal |
| `terminal/wait_for_exit` | Server → Client | Wait for process exit |
| `terminal/kill` | Server → Client | Kill terminal process |
---
## Local Facade
```python
from cleveragents.acp import AcpLocalFacade, AcpRequest
`AcpLocalFacade` handles extension method dispatch in local mode:
facade = AcpLocalFacade()
response = facade.dispatch(AcpRequest(operation="session.create"))
assert response.status == "ok"
```python
from cleveragents.acp import AcpLocalFacade
facade = AcpLocalFacade(services={
"session_service": my_session_service,
"plan_lifecycle_service": my_plan_lifecycle_service,
"tool_registry": my_tool_registry,
"resource_registry_service": my_resource_registry_service,
})
# Extension methods are dispatched to in-process services
result = await facade.dispatch("_cleveragents/plan/status", {"plan_id": "01J…"})
```
### Constructor
@@ -58,196 +212,110 @@ assert response.status == "ok"
| Method | Returns | Description |
|---------------------|-------------------|--------------------------------------|
| `dispatch(request)` | `AcpResponse` | Route request to handler |
| `dispatch(method, params)` | `dict` | Route extension method to handler |
| `register_service` | `None` | Register a named service |
| `list_operations` | `list[str]` | All supported operation names |
| `list_methods` | `list[str]` | All supported extension method names |
---
## Service Wiring
Each ACP operation is wired to a concrete application service. Services
are injected via the `services` dict at construction time or registered
later with `register_service()`. When a service key is absent, the
handler falls back to a safe stub response.
Each ACP extension method is wired to a concrete application service.
Services are injected via the `services` dict at construction time or
registered later with `register_service()`.
### Service Keys
| Key | Type | Wired Operations |
|------------------------------|-----------------------------|-------------------------------|
| `session_service` | `SessionService` | `session.create`, `session.close` |
| `plan_lifecycle_service` | `PlanLifecycleService` | `plan.create`, `plan.execute`, `plan.status`, `plan.diff`, `plan.apply` |
| `tool_registry` | `ToolRegistry` | `registry.list_tools` |
| `resource_registry_service` | `ResourceRegistryService` | `registry.list_resources` |
| `event_queue` | `AcpEventQueue` | `event.subscribe` |
### Wired Operation Details
| Operation | Service | Service Method | Required Params |
|------------------------|---------------------------|-------------------------|------------------------------------|
| `session.create` | `SessionService` | `create(actor_name=…)` | `actor_name` (optional) |
| `session.close` | `SessionService` | `delete(session_id)` | `session_id` |
| `plan.create` | `PlanLifecycleService` | `use_action(…)` | `action_name` |
| `plan.execute` | `PlanLifecycleService` | `execute_plan(plan_id)` | `plan_id` |
| `plan.status` | `PlanLifecycleService` | `get_plan(plan_id)` | `plan_id` |
| `plan.diff` | `PlanLifecycleService` | `get_plan(plan_id)` | `plan_id` |
| `plan.apply` | `PlanLifecycleService` | `apply_plan(plan_id)` | `plan_id` |
| `registry.list_tools` | `ToolRegistry` | `list_tools(namespace=…)` | `namespace` (optional) |
| `registry.list_resources` | `ResourceRegistryService` | `list_resources(type_name=…)` | `type_name` (optional) |
| `context.get` | *(stub)* | N/A | — |
| `event.subscribe` | `AcpEventQueue` | `subscribe_local(cb)` | — |
!!! note "context.get"
`context.get` currently returns a stub response (`{"context": {}, "stub": true}`)
pending the completion of the ACMS `ContextAssemblyPipeline`.
### Example: Wired Facade
```python
from cleveragents.acp.facade import AcpLocalFacade
from cleveragents.acp.events import AcpEventQueue
from cleveragents.acp.models import AcpRequest
facade = AcpLocalFacade(services={
"session_service": my_session_service,
"plan_lifecycle_service": my_plan_lifecycle_service,
"tool_registry": my_tool_registry,
"resource_registry_service": my_resource_registry_service,
"event_queue": AcpEventQueue(),
})
response = facade.dispatch(
AcpRequest(operation="plan.status", params={"plan_id": "01J…"})
)
assert response.status == "ok"
assert "phase" in response.data
```
---
## 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`, `state` |
| `plan.diff` | `plan_id`, `changes`, `phase` |
| `plan.apply` | `plan_id`, `status` |
| `registry.list_tools` | `tools` (list of `{name, description}`) |
| `registry.list_resources` | `resources` (list of `{resource_id, name, type_name}`) |
| `context.get` | `context`, `stub`, `message` |
| `event.subscribe` | `subscription_id`, `status` |
Unknown operations raise `AcpOperationNotFoundError`.
| Key | Type | Wired Methods |
|------------------------------|-----------------------------|-------------------------------------|
| `session_service` | `SessionWorkflow` | Standard `session/*` methods |
| `plan_lifecycle_service` | `PlanLifecycleService` | `_cleveragents/plan/*` |
| `tool_registry` | `ToolRegistry` | `_cleveragents/registry/tool/*` |
| `resource_registry_service` | `ResourceRegistryService` | `_cleveragents/registry/resource/*` |
| `sync_service` | `SyncService` | `_cleveragents/sync/*` |
| `namespace_service` | `NamespaceService` | `_cleveragents/namespace/*` |
| `context_service` | `ContextService` | `_cleveragents/context/*` |
---
## Error Code Taxonomy
Domain exceptions are mapped to ACP error codes via `map_domain_error()`:
Domain exceptions are mapped to JSON-RPC 2.0 error codes:
| ACP Error Code | Domain Exception(s) | Meaning |
|-----------------------|----------------------------------------|----------------------------------|
| `NOT_FOUND` | `ResourceNotFoundError` | Requested entity does not exist |
| `VALIDATION_ERROR` | `ValidationError` | Input failed validation |
| `PLAN_ERROR` | `PlanError` | Plan lifecycle fault |
| `INVALID_STATE` | `BusinessRuleViolation` | State precondition not met |
| `AUTH_ERROR` | `AuthenticationError` | Authentication failure |
| `FORBIDDEN` | `AuthorizationError` | Insufficient permissions |
| `CONFIGURATION_ERROR` | `ConfigurationError` | Configuration missing or invalid |
| `INTERNAL_ERROR` | Any other `CleverAgentsError` or `Exception` | Unexpected server error |
| JSON-RPC Code | Domain Exception(s) | Meaning |
|-------------------|----------------------------------------|----------------------------------|
| `-32700` | — | Parse error (malformed JSON) |
| `-32600` | — | Invalid request |
| `-32601` | `AcpOperationNotFoundError` | Method not found |
| `-32602` | `ValidationError` | Invalid params |
| `-32603` | Any unhandled `Exception` | Internal error |
| `-32001` | `ResourceNotFoundError` | Entity not found |
| `-32002` | `AuthenticationError` | Authentication required |
| `-32003` | `AuthorizationError` | Authorization forbidden |
| `-32004` | `BusinessRuleViolation` | Invalid state |
| `-32005` | `DuplicateEntityError` | Already exists |
| `-32006` | `BudgetExceededError` | Budget exceeded |
| `-32007` | `AcpVersionMismatchError` | Version mismatch |
| `-32008` | `PlanError` | Plan lifecycle error |
Error details are returned in the `AcpResponse.error` field as an
`AcpErrorDetail` with the `code` and `message` fields populated.
### Example Error Response
---
## 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
```json
{
"jsonrpc": "2.0",
"id": 42,
"error": {
"code": -32001,
"message": "Plan not found",
"data": { "plan_id": "01HXRCF1..." }
}
}
```
Supported versions: `["1.0"]`
---
## Authentication
Authentication follows the ACP standard connection lifecycle:
1. Client sends `initialize` with capabilities
2. Server responds with its capabilities (including `_cleveragents` extensions)
3. Client sends `authenticate` with `{ "token": "<server.token>" }`
4. Server validates and returns authentication status
Local mode (stdio) bypasses authentication — the agent subprocess runs
with the user's local permissions.
---
## Models
## Client Architecture
### AcpRequest
The client uses an `AcpClient` wrapping the ACP Python SDK:
| 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` |
```python
from cleveragents.acp import AcpClient, TransportSelector
### AcpResponse
# TransportSelector picks stdio or HTTP based on configuration
transport = TransportSelector().get_transport()
client = AcpClient(transport)
| 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` |
# Standard ACP method
await client.prompt(session_id="ses_01HXR...", message="Add pagination")
### AcpErrorDetail
# Extension method
result = await client.plan_status(plan_id="01HXRCF1...")
```
| Field | Type | Default |
|-----------|-------------------|---------|
| `code` | `str` | *required* |
| `message` | `str` | *required* |
| `details` | `dict[str, Any]` | `{}` |
### Component Evolution
### 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 |
| Previous Component | Current Replacement | Notes |
|-------------------|-------------------|-------|
| `AcpLocalFacade` | **Retained** | Adapts to ACP method signatures |
| `AcpHttpTransport` (stub) | ACP SDK `ClientSideConnection` | SDK provides production transport |
| `AcpRequest` / `AcpResponse` | ACP SDK JSON-RPC types | Native SDK types |
| `AcpEvent` | `session/update` notifications | Mapped to ACP notification types |
| `AcpEventQueue` | **Retained** for local mode | In-process notification delivery |
| `AcpVersionNegotiator` | `initialize` handshake | Per ACP standard |
---
@@ -263,6 +331,6 @@ CleverAgentsError
| Exception | When Raised |
|-----------------------------|-------------------------------------------|
| `AcpNotAvailableError` | Server-mode operation in local mode |
| `AcpVersionMismatchError` | Unsupported ACP version requested |
| `AcpOperationNotFoundError` | Unknown operation dispatched |
| `AcpNotAvailableError` | Server-mode operation attempted without connection |
| `AcpVersionMismatchError` | Unsupported ACP version during `initialize` |
| `AcpOperationNotFoundError` | Unknown method dispatched |
+557 -237
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -67,6 +67,8 @@ nav:
- ADR-044 TUI Architecture and Framework: adr/ADR-044-tui-architecture-and-framework.md
- ADR-045 TUI Persona System: adr/ADR-045-tui-persona-system.md
- ADR-046 TUI Reference and Command System: adr/ADR-046-tui-reference-and-command-system.md
- ADR-047 ACP Standard Adoption: adr/ADR-047-acp-standard-adoption.md
- ADR-048 Server Application Architecture: adr/ADR-048-server-application-architecture.md
theme:
name: material