diff --git a/docs/adr/ADR-001-layered-architecture.md b/docs/adr/ADR-001-layered-architecture.md index c9ec6ff9..cdd6ccc7 100644 --- a/docs/adr/ADR-001-layered-architecture.md +++ b/docs/adr/ADR-001-layered-architecture.md @@ -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 @@ -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: diff --git a/docs/adr/ADR-005-technical-stack.md b/docs/adr/ADR-005-technical-stack.md index 69938c57..0de0c7c3 100644 --- a/docs/adr/ADR-005-technical-stack.md +++ b/docs/adr/ADR-005-technical-stack.md @@ -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 diff --git a/docs/adr/ADR-021-cli-and-output-rendering.md b/docs/adr/ADR-021-cli-and-output-rendering.md index e574af82..0afb14f7 100644 --- a/docs/adr/ADR-021-cli-and-output-rendering.md +++ b/docs/adr/ADR-021-cli-and-output-rendering.md @@ -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. diff --git a/docs/adr/ADR-022-langchain-langgraph-integration.md b/docs/adr/ADR-022-langchain-langgraph-integration.md index 947da2a1..8ff6f60b 100644 --- a/docs/adr/ADR-022-langchain-langgraph-integration.md +++ b/docs/adr/ADR-022-langchain-langgraph-integration.md @@ -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 diff --git a/docs/adr/ADR-023-server-mode.md b/docs/adr/ADR-023-server-mode.md index 3b9afd8e..998afa4c 100644 --- a/docs/adr/ADR-023-server-mode.md +++ b/docs/adr/ADR-023-server-mode.md @@ -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 @@ -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 (`/`, `/`) 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 invocation ↔ HTTPS 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. diff --git a/docs/adr/ADR-026-agent-client-protocol.md b/docs/adr/ADR-026-agent-client-protocol.md index 786d76b6..2adba89a 100644 --- a/docs/adr/ADR-026-agent-client-protocol.md +++ b/docs/adr/ADR-026-agent-client-protocol.md @@ -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 @@ -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 `, 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 `, 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": "", - "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": "", - "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": "", - "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 ` 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 ` 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. diff --git a/docs/adr/ADR-044-tui-architecture-and-framework.md b/docs/adr/ADR-044-tui-architecture-and-framework.md index 76781ec9..fcdfdbd6 100644 --- a/docs/adr/ADR-044-tui-architecture-and-framework.md +++ b/docs/adr/ADR-044-tui-architecture-and-framework.md @@ -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 diff --git a/docs/adr/ADR-047-acp-standard-adoption.md b/docs/adr/ADR-047-acp-standard-adoption.md new file mode 100644 index 00000000..8ffb6912 --- /dev/null +++ b/docs/adr/ADR-047-acp-standard-adoption.md @@ -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 Presentation–Application 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 " + 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 Presentation–Application 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 Presentation–Application 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": "" }` +4. Server validates and returns authentication status + +For HTTP transport, the token is also sent as `Authorization: Bearer ` 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. diff --git a/docs/adr/ADR-048-server-application-architecture.md b/docs/adr/ADR-048-server-application-architecture.md new file mode 100644 index 00000000..502e12e1 --- /dev/null +++ b/docs/adr/ADR-048-server-application-architecture.md @@ -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 " + 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": "" }` +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 (`/`, `/`) 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. diff --git a/docs/gen_ref_pages.py b/docs/gen_ref_pages.py index 2bd79e05..328abbd8 100644 --- a/docs/gen_ref_pages.py +++ b/docs/gen_ref_pages.py @@ -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: diff --git a/docs/reference/acp.md b/docs/reference/acp.md index 5a809cb5..df1daeb3 100644 --- a/docs/reference/acp.md +++ b/docs/reference/acp.md @@ -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": "" }` +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 | diff --git a/docs/specification.md b/docs/specification.md index 9478bedb..7a9810e6 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -52,7 +52,7 @@ The following standards are integrated into the architecture: ??? info "Agent Client Protocol (ACP) -- Details" - Defines the versioned client-server contract for sessions, plan lifecycle, registry access, context operations, and event streaming across all Presentation-layer clients (CLI, TUI, Web, IDE). ACP is the **fundamental boundary between the Presentation and Application layers** — every client operation flows through ACP regardless of deployment mode. In local mode, ACP maps to in-process service facade calls (no network, no serialization). In server mode, ACP is implemented by the REST API over HTTPS with JSON envelopes and token-based authentication. This transport duality makes clients interchangeable, enables reliable remote execution in server mode, and supports third-party client development against a stable, versioned surface. The IDE plugin (an embedded TUI) communicates with the backend exclusively through ACP. See [ACP Integration Architecture](#acp-integration-architecture) for the full architectural deep-dive, including operation routing, envelope format, event streaming, error taxonomy, and versioning protocol. + 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 defines the **fundamental boundary between the Presentation and Application layers** — every client operation flows through ACP regardless of deployment mode. The standard provides methods for session lifecycle, prompting, streaming updates, tool calls, permission requests, and file/terminal callbacks. CleverAgents extends the standard with `_cleveragents/`-prefixed extension methods (per the ACP extensibility mechanism) for platform operations: plan lifecycle, registry CRUD, entity sync, namespace management, and diagnostics. In local mode, ACP flows over **stdio** (agent as subprocess) with platform operations resolved in-process via `AcpLocalFacade`. In server mode, ACP flows over **HTTP** to the CleverAgents server. Both transports use the ACP Python SDK. All clients — CLI, TUI, IDE plugin, and third-party — communicate exclusively through ACP. See [ADR-047](adr/ADR-047-acp-standard-adoption.md), [ADR-048](adr/ADR-048-server-application-architecture.md), and [Server and Client Architecture](#server-and-client-architecture) for the full detail. ??? info "Model Context Protocol (MCP) -- Details" @@ -163,7 +163,7 @@ The following standards are integrated into the architecture: ???+ abstract "Protocols & Standards" ACP (Agent Client Protocol) - : A versioned client-server protocol that defines the **fundamental boundary between the Presentation and Application layers**. Covers authentication, session lifecycle, plan operations, registry access, context operations, and event streaming for all Presentation-layer clients (CLI, TUI, Web, IDE plugin). In local mode ACP maps to in-process service facade calls; in server mode it is implemented by the REST API over HTTPS. Every CLI command maps 1:1 to an ACP operation. See [Core Concepts > Server > ACP](#agent-client-protocol-acp) and [Architecture > ACP Integration Architecture](#acp-integration-architecture) for full detail. + : The external **Agent Client Protocol** standard ([agentclientprotocol.org](https://agentclientprotocol.org)), built on **JSON-RPC 2.0**, used as the **sole** communication protocol for all client-server interaction. ACP defines the **fundamental boundary between the Presentation and Application layers**. Standard ACP methods handle agent conversations (sessions, prompts, streaming, tool calls, permissions, file/terminal callbacks). CleverAgents `_cleveragents/`-prefixed extension methods handle platform operations (plan lifecycle, registry CRUD, entity sync, namespace management, diagnostics). In local mode ACP flows over stdio (agent as subprocess); in server mode over HTTP. Every CLI command maps to an ACP method. See [Core Concepts > Server > ACP](#agent-client-protocol-acp) and [Server and Client Architecture](#server-and-client-architecture) for full detail. LSP (Language Server Protocol) : A standard protocol for language intelligence, used in CleverAgents to attach semantic code understanding to actors and agents. LSP servers are registered in the global **LSP Registry** (namespaced as `[[server:]namespace/]name`) and bound to actor graph nodes via YAML configuration. Capabilities — diagnostics, type information, symbol navigation, completions, references, rename, code actions — are exposed as tools (via `LSPToolAdapter`) and as automatic context enrichment. Actors can bind LSP servers explicitly by name, by language, or automatically based on detected resource languages. The LSP Runtime in the Infrastructure layer manages server lifecycle, workspace mapping, and file synchronization. See [LSP Integration](#lsp-integration) for full detail. @@ -23301,158 +23301,260 @@ The notes include a known issue: conversation history can be lost between CLI in ### Server !!! adr "Architecture Decision" - The server architecture, multi-user support, and local/server mode duality are defined in [ADR-023: Server Mode](adr/ADR-023-server-mode.md). + The server architecture, multi-user support, and local/server mode duality are defined in [ADR-023: Server Mode](adr/ADR-023-server-mode.md). The server application architecture is defined in [ADR-048: Server Application Architecture](adr/ADR-048-server-application-architecture.md). The ACP standard adoption is defined in [ADR-047: ACP Standard Adoption](adr/ADR-047-acp-standard-adoption.md). #### What a Server Is -A server is an optional mode that enables: +A server is an **optional, separate application** that enables: * multi-user access * shared org namespaces (`/` and `/`) -* persistent plan records -* remote plan execution -* governance +* persistent plan records (PostgreSQL) +* remote plan execution (via LangGraph Platform) +* governance and auditing +* entity synchronization across devices and team members -**Single-user local mode is the default** (so setup is easy), but the architecture anticipates server mode for shared skills and org-level actions. +The server shares the same **Domain and Application layers** as the client (per ADR-001), differing only in Infrastructure and Presentation layers. Its sole client-facing interface is an **ACP JSON-RPC 2.0 endpoint** — there is no REST API. + +**Single-user local mode is the default** (so setup is easy), but the architecture anticipates server mode for shared skills, org-level actions, and collaborative workflows. #### Client-Only vs Server Mode -| Mode | Description | Plan Execution | -|------|-------------|----------------| -| **Client-only** | No server connection. All data in local database. | Always local | -| **Server mode** | Connected to a CleverAgents server. Namespaced items sync. | Local or server | +| Mode | Description | Protocol | Plan Execution | +|------|-------------|----------|----------------| +| **Client-only** | No server connection. All data in local database. Agent runs as local subprocess. | ACP over stdio | Always local | +| **Server mode** | Connected to a CleverAgents server. Namespaced items sync. | ACP over HTTP | Local or server (via LangGraph Platform RemoteGraph) | **It is possible to run a client with no server at all.** Server is optional. +#### Server Configuration and Connection + +Connecting to a server requires two configuration keys: + +| Key | Env Var | Purpose | +|-----|---------|---------| +| `server.url` | `CLEVERAGENTS_SERVER_URL` | URL of the CleverAgents ACP server endpoint | +| `server.token` | `CLEVERAGENTS_SERVER_TOKEN` | Authentication token (obtained via server registration or team invite) | + +When `server.url` is set, the client switches to server mode: ACP methods flow over HTTP instead of stdio, and server namespaces become available. The connection lifecycle follows the ACP standard: `initialize` (capability exchange) → `authenticate` (token validation) → ready for operations. + +#### Cloud Plan Execution + +In server mode, actor graphs are deployed to **LangGraph Platform** and invoked via **RemoteGraph**. This means different actors for different plan phases (strategy, execution, estimation) each deploy as separate RemoteGraphs, enabling independent scaling without limiting plan capabilities. + +When an agent executing on the server needs to access **client-local resources** (files, terminals), it uses ACP callbacks (`fs/read_text_file`, `fs/write_text_file`, `terminal/*`) which the client handles locally. This enables server-hosted plan execution even when some resources exist only on the client machine. + +#### Entity Sync and Sharing + +Entity synchronization between client and server uses ACP extension methods (`_cleveragents/sync/*`): + +* **Auto-sync** (`server.sync.auto`): Entities sync on connection and at `server.sync.interval` (default: 300s) +* **Pull**: Server namespace entities are downloaded to local cache +* **Push**: Local entity definitions can be explicitly pushed to a server namespace +* **Status**: Compare local and server entity versions to detect drift +* The `local/` namespace is **never** synced — it exists only on the client + +#### Multi-Device Experience + +With server mode, a user can: + +* Start a plan on their laptop, close the lid, and monitor progress from another device +* Share entity definitions (actors, skills, actions) across team members via server namespaces +* Run long-running plans on server infrastructure without keeping a client connected +* Access the same session history from CLI, TUI, or IDE plugin on different machines + +#### IDE Integration via ACP + +IDE plugins (e.g., VS Code, JetBrains) communicate with CleverAgents exclusively through ACP — the same protocol used by CLI and TUI. The IDE plugin can operate in either local mode (spawning an agent subprocess over stdio) or server mode (connecting to a remote CleverAgents server over HTTP). The ACP standard's file and terminal callbacks (`fs/*`, `terminal/*`) allow the agent to interact with the IDE's workspace and integrated terminal. + #### Agent Client Protocol (ACP) !!! adr "Architecture Decision" - The Agent Client Protocol is defined in [ADR-026: Agent Client Protocol (ACP)](adr/ADR-026-agent-client-protocol.md). + The Agent Client Protocol is defined in [ADR-026: Agent Client Protocol (ACP)](adr/ADR-026-agent-client-protocol.md). The adoption of the external ACP standard is defined in [ADR-047: ACP Standard Adoption](adr/ADR-047-acp-standard-adoption.md). -ACP is the **versioned client-server contract** between every Presentation-layer client (CLI, TUI, Web, IDE plugin) and the CleverAgents backend. It exists to solve a fundamental architectural problem: CleverAgents has *multiple presentation surfaces* (CLI, TUI, Web, IDE) and *two deployment modes* (local and server), but every client must observe identical behavior regardless of how or where it connects. Without a shared protocol surface, each client would require bespoke integration code that drifts from the core domain model, making interoperability fragile and third-party client development impractical. +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 serves as the **fundamental boundary between the Presentation and Application layers** — every client operation flows through ACP regardless of deployment mode. No client ever bypasses ACP to touch storage, repositories, or internal domain services directly. -ACP eliminates this problem by providing a single, transport-agnostic, versioned operation surface that all clients program against. No client ever bypasses ACP to touch storage, repositories, or internal domain services directly. +ACP solves a fundamental architectural problem: CleverAgents has *multiple presentation surfaces* (CLI, TUI, IDE plugin) and *multiple deployment modes* (local and server), but every client must observe identical behavior regardless of how or where it connects. The external ACP standard provides a mature, ecosystem-aligned protocol surface that third parties can implement against. -!!! tip "For the complete architectural deep-dive — transport duality, operation routing, envelope format, event streaming, authentication, versioning, and error handling — see the [ACP Integration Architecture](#acp-integration-architecture) section under Architecture." +!!! tip "For the complete architectural deep-dive — transport modes, method catalog, extension methods, wire format, authentication, and error handling — see the [Server and Client Architecture](#server-and-client-architecture) section under Architecture." -##### ACP Operation Groups +##### Standard ACP Methods -ACP organizes its operations into five groups, each mapping to one or more Application-layer services: +The external ACP standard defines methods for the core agent conversation lifecycle. These map directly to CleverAgents concepts: -| 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) | +| ACP Standard Method | Direction | CleverAgents Mapping | +| :------------------ | :-------- | :------------------- | +| `initialize` | Client → Server | Capability negotiation — advertises supported `_cleveragents/` extensions, automation profiles, tool registries | +| `authenticate` | Client → Server | Token-based authentication (`server.token`) | +| `session/new` | Client → Server | `SessionWorkflow.create()` — create a new conversation session | +| `session/load` | Client → Server | `SessionWorkflow.resume()` — resume an existing session | +| `session/list` | Client → Server | `SessionWorkflow.list()` — list available sessions | +| `session/prompt` | Client → Server | `SessionWorkflow.tell()` — send user message to orchestrator actor | +| `session/cancel` | Client → Server | Cancel in-progress plan execution | +| `session/set_mode` | Client → Server | Automation profile switching (manual → supervised → autonomous) | +| `session/set_model` | Client → Server | LLM provider/model switching | +| `session/fork` | Client → Server | Fork a session for branching exploration | +| `session/resume` | Client → Server | Resume a paused session | -Every CLI command listed in the [CLI Commands](#cli-commands) section maps 1:1 to an ACP operation. When a user runs `agents plan status `, the CLI constructs an ACP `plan.status` request, sends it through the active transport, and renders the response. The CLI is a thin rendering layer — it contains no business logic. +##### ACP Streaming Notifications -##### Transport Duality +The ACP standard defines `session/update` notifications (JSON-RPC 2.0 notifications — no response expected) for real-time streaming from server to client: -ACP is **transport-agnostic** at the specification level. The system ships with two transports: +| Notification Type | CleverAgents Mapping | +| :---------------- | :------------------- | +| `agent_message_chunk` | Streaming agent response tokens during plan execution | +| `user_message_chunk` | Echo of user input | +| `plan` | Plan state with entries (content, priority, status) — maps directly to the CleverAgents plan concept | +| `tool_call` | Tool invocation started during plan execution | +| `tool_call_update` | Tool invocation progress/result | +| `mode_change` | Automation profile changed | -| Mode | Transport | Serialization | Authentication | -| :--- | :-------- | :------------ | :------------- | -| **Local** | Direct Python method calls on the Service Facade | None (native objects) | Bypassed | -| **Server** | HTTPS + JSON via the FastAPI REST API | JSON request/response envelopes | Token-based (`server.token`) | +Events within a single plan lifecycle preserve **causal ordering**: a phase transition notification always arrives after the corresponding prior phase completion. -In **local mode**, the CLI (or TUI, or any in-process client) calls the Service Facade directly — `PlanService.create_plan()`, `SessionWorkflow.tell()`, etc. There is no serialization, no network, and no authentication overhead. The ACP operation surface is preserved as a logical contract: the same operation names, the same parameter shapes, and the same response semantics apply. +##### ACP Callbacks (Server → Client) -In **server mode**, the CLI becomes a thin HTTPS client. Each ACP operation is serialized into a JSON request envelope, sent to the REST API endpoint, and deserialized on the server side into the same Service Facade call. Responses follow a matching JSON envelope with `status`, `data`, and `error` fields. This symmetry means a test suite written against ACP operations passes identically in both modes. +The ACP standard supports server-to-client requests for operations that require client-side resources: + +| Callback | Purpose | +| :------- | :------ | +| `session/request_permission` | Human-in-the-loop approval — maps to automation profile gates | +| `fs/read_text_file` | Agent reads a file on the client machine (local project resources) | +| `fs/write_text_file` | Agent writes a file on the client machine | +| `terminal/create` | Agent requests a terminal on the client machine (sandbox execution) | +| `terminal/output` | Terminal output streaming | +| `terminal/release` / `terminal/wait_for_exit` / `terminal/kill` | Terminal lifecycle management | + +These callbacks enable a server-hosted agent to access client-local resources without requiring the server to have direct access to the user's filesystem or terminal. + +##### CleverAgents Extension Methods + +Platform operations beyond the core agent conversation use ACP **extension methods** — `_`-prefixed methods per the ACP extensibility specification. All CleverAgents extensions use the `_cleveragents/` namespace: + +| 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` (for each entity type: actor, skill, tool, validation, resource, resource_type, project, action, automation_profile, invariant, lsp) | `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 listed in the [CLI Commands](#cli-commands) section maps to either a standard ACP method or a `_cleveragents/` extension method. When a user runs `agents plan status `, the CLI sends a `_cleveragents/plan/status` JSON-RPC request through the active transport and renders the response. The CLI is a thin rendering layer — it contains no business logic. + +##### Transport Modes + +ACP operates over two transports provided by the ACP Python SDK: + +| Mode | Transport | How It Works | Authentication | +| :--- | :-------- | :----------- | :------------- | +| **Local** | ACP over **stdio** | Client spawns agent as subprocess; JSON-RPC messages flow over stdin/stdout. Platform extension methods are resolved in-process via `AcpLocalFacade`. | Bypassed (local user permissions) | +| **Server** | ACP over **HTTP** | Client connects to CleverAgents server via ACP SDK HTTP transport. All methods (standard + extensions) flow through the single ACP endpoint. | Token-based via `authenticate` method + `Authorization: Bearer` header | + +In **local mode**, the agent runs as a subprocess. Standard ACP methods drive the conversation. Extension methods (`_cleveragents/*`) are intercepted by `AcpLocalFacade` and routed to in-process Application-layer services. No serialization beyond JSON-RPC framing, no network, no authentication overhead. + +In **server mode**, the client connects to the CleverAgents server. All communication — both agent conversations and platform operations — flows through the single ACP JSON-RPC 2.0 endpoint. The server delegates actor execution to LangGraph Platform via RemoteGraph. + +A third configuration supports **external ACP agents**: standard ACP methods go to the external agent's server, while `_cleveragents/` extension methods go to the CleverAgents server. This enables interoperability with any ACP-compliant agent. ```kroki-mermaid sequenceDiagram participant U as User - participant C as CLI / TUI / Web / IDE - participant ACP as ACP Layer - participant SF as Service Facade - participant D as Domain + Infrastructure + participant C as CLI / TUI / IDE + participant ACP as ACP Client (SDK) + participant S as ACP Server + participant LG as LangGraph Platform rect rgb(220, 240, 255) - Note over C,SF: Local Mode (in-process) + Note over C,ACP: Local Mode (stdio) U->>C: agents plan status - C->>ACP: plan.status(plan_id) - ACP->>SF: PlanService.get_status(plan_id) - SF->>D: Plan.query(plan_id) - D-->>SF: Plan state - SF-->>ACP: PlanStatusResponse - ACP-->>C: Structured result + C->>ACP: _cleveragents/plan/status {JSON-RPC 2.0} + ACP->>ACP: AcpLocalFacade → PlanService.get_status() + ACP-->>C: JSON-RPC result C-->>U: Rendered output end rect rgb(255, 235, 220) - Note over C,SF: Server Mode (HTTPS) - U->>C: agents plan status - C->>ACP: plan.status(plan_id) - ACP->>SF: POST /api/v1/plan/status {JSON envelope} - SF->>D: Plan.query(plan_id) - D-->>SF: Plan state - SF-->>ACP: JSON response envelope - ACP-->>C: Structured result + Note over C,LG: Server Mode (HTTP) + U->>C: agents session tell "Refactor auth" + C->>ACP: session/prompt {JSON-RPC 2.0} + ACP->>S: HTTP POST (JSON-RPC) + S->>LG: RemoteGraph.invoke(actor_graph) + LG-->>S: Actor result + S-->>ACP: session/update notifications (streaming) + ACP-->>C: Streaming response C-->>U: Rendered output end ``` -##### ACP Request/Response Envelope +##### Wire Format -Every ACP operation uses a canonical JSON envelope. This applies to all server-mode requests and is the logical shape in local mode (where it maps to method signatures): +All ACP communication uses **JSON-RPC 2.0** framing: -**Request envelope:** +**Request:** ```json { - "acp_version": "1.0", - "request_id": "", - "operation": "plan.status", - "params": { "plan_id": "01HXRCF1..." }, - "auth": { "token": "..." } + "jsonrpc": "2.0", + "id": 1, + "method": "session/prompt", + "params": { "session_id": "ses_01HXR...", "message": "Refactor the auth module" } } ``` -**Response envelope:** +**Response:** ```json { - "acp_version": "1.0", - "request_id": "", - "status": "ok", - "data": { "plan_id": "01HXRCF1...", "phase": "execute", "state": "running" }, - "error": null, - "timing": { "duration_ms": 42 } + "jsonrpc": "2.0", + "id": 1, + "result": { "status": "accepted" } } ``` -Error responses carry a structured `error` object with `code`, `message`, and optional `details`: +**Notification (streaming, no `id`):** ```json { - "acp_version": "1.0", - "request_id": "", - "status": "error", - "data": null, - "error": { "code": "PLAN_NOT_FOUND", "message": "No plan with ID 01HXRCF1...", "details": {} } + "jsonrpc": "2.0", + "method": "session/update", + "params": { "session_id": "ses_01HXR...", "type": "agent_message_chunk", "data": { "content": "I'll start by..." } } } ``` -##### Event Streaming +**Extension method request:** +```json +{ + "jsonrpc": "2.0", + "id": 42, + "method": "_cleveragents/plan/status", + "params": { "plan_id": "01HXRCF1..." } +} +``` -ACP provides a real-time event channel for long-running operations. In server mode, events are delivered via **Server-Sent Events (SSE)** over a persistent HTTPS connection. In local mode, events are delivered via in-process subscription (RxPY observable or callback). Every event carries: +**Error response:** +```json +{ + "jsonrpc": "2.0", + "id": 42, + "error": { "code": -32001, "message": "Plan not found", "data": { "plan_id": "01HXRCF1..." } } +} +``` -* **`event_type`** — e.g., `plan.phase_changed`, `tool.invoked`, `validation.completed`, `plan.decision_made` -* **`plan_id`** — the plan context (if applicable) -* **`timestamp`** — ISO 8601 -* **`payload`** — structured event-specific data +##### Authentication -Events within a single plan lifecycle preserve **causal ordering**: a `plan.phase_changed` event for the Execute phase always arrives after the corresponding Strategize completion event. Clients can subscribe to events filtered by plan ID, event type, or both. +Authentication uses the ACP standard `authenticate` method: + +1. Client calls `initialize` — capability negotiation (includes supported `_cleveragents` extensions) +2. Server responds with its capabilities +3. Client calls `authenticate` with `{ "token": "" }` +4. Server validates and returns authentication status + +For HTTP transport, the token is also sent as `Authorization: Bearer ` header on every request. Local mode (stdio) bypasses authentication entirely — the agent subprocess runs with the user's local permissions. ##### Versioning -ACP is versioned to protect clients from breaking changes: - -* 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. - -This ensures that older clients continue to function during rolling upgrades and that third-party clients have a stable development target. +- 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` +- 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 #### Plan Execution Location @@ -42771,10 +42873,10 @@ CleverAgents is architected as a **layered, event-driven application** following The architecture supports two deployment modes: -1. **Local Mode**: A single-process CLI application where all components run in the same Python process. The CLI is the primary entry point, the database is SQLite, and all resources are local. -2. **Server Mode**: A multi-user service where the CLI acts as a thin client communicating with a remote server over HTTPS. The server hosts shared storage, namespace resolution, and remote plan execution. +1. **Local Mode**: A single-process CLI application where all components run in the same Python process. The CLI is the primary entry point, the database is SQLite, and all resources are local. ACP flows over **stdio** (agent as subprocess). +2. **Server Mode**: A multi-user service where the CLI acts as a thin client communicating with a remote CleverAgents server via **ACP over HTTP** (JSON-RPC 2.0). The server hosts shared storage (PostgreSQL), namespace resolution, and remote plan execution (via LangGraph Platform RemoteGraph). -Both modes share the same domain model and application layer. The infrastructure layer provides swappable implementations for each deployment target. +Both modes share the same domain model and application layer. The infrastructure layer provides swappable implementations for each deployment target. All communication uses the **Agent Client Protocol** standard — there is no REST API. #### High-Level Component Diagram @@ -42792,11 +42894,11 @@ package "Presentation Layer" <> #E0F7FA { component [TUI\n(Textual)] as TUI component [Web\n(Textual Web)] as Web component [IDE Plugin\n(Embedded TUI)] as IDE - component [REST API\n(uvicorn / FastAPI)] as REST + component [ACP Server\n(JSON-RPC 2.0 Endpoint)] as ACPSRV } interface "ACP\n(Agent Client Protocol)" as ACP #FFD54F -note right of ACP : Local: in-process calls\nServer: HTTPS + JSON\nVersioned contract (v1.0) +note right of ACP : Local: ACP over stdio\nServer: ACP over HTTP\nJSON-RPC 2.0 wire format package "Application Layer" <> #E3F2FD { package "Service Facade" { @@ -42862,13 +42964,13 @@ package "Infrastructure Layer" <> #FFEBEE { CLI -[hidden]right-> TUI TUI -[hidden]right-> Web Web -[hidden]right-> IDE -IDE -[hidden]right-> REST +IDE -[hidden]right-> ACPSRV CLI -down-> ACP TUI -down-> ACP Web -down-> ACP IDE -down-> ACP -REST -down-> ACP +ACPSRV -down-> ACP ACP -down-> PS ACP -down-> SW @@ -42888,7 +42990,7 @@ DS -down-> IDX Each adopted standard maps to an explicit architectural boundary: -* **ACP (Agent Client Protocol)** — connects Presentation-layer clients (CLI, TUI, Web, IDE) to the Service Facade / REST API. Covers sessions, plans, registries, and event streaming. In local mode it resolves to in-process calls; in server mode it is the HTTPS contract. This keeps clients interchangeable and supports third-party client development. +* **ACP (Agent Client Protocol)** — the external [ACP standard](https://agentclientprotocol.org) (JSON-RPC 2.0) connects Presentation-layer clients (CLI, TUI, IDE) to the Application layer. Standard ACP methods handle agent conversations (sessions, prompts, streaming, tool calls, permissions). CleverAgents `_cleveragents/`-prefixed extension methods handle platform operations (plans, registries, context, sync). In local mode ACP flows over stdio; in server mode over HTTP. This keeps clients interchangeable and supports third-party client and agent development. * **LSP (Language Server Protocol)** — LSP servers are registered in the LSP Registry (Infrastructure layer) and bound to actor graph nodes to provide language intelligence. The `LSPToolAdapter` exposes LSP capabilities as tools (diagnostics, hover, go-to-definition, completions, references, rename) that actors call during reasoning, and the LSP Runtime automatically enriches actor context with diagnostic and type information. This gives actors the same semantic code understanding that human developers get from IDEs. See [LSP Integration](#lsp-integration). * **MCP (Model Context Protocol)** — external MCP servers reside in the Infrastructure layer. The MCPToolAdapter bridges them into first-class Tools in the Domain model with extended capability metadata. MCP tools are composed into skills and used as tool nodes in actor graphs, giving the system standardized tool discovery and invocation. * **Agent Skills ([https://AgentSkills.io](https://AgentSkills.io))** — Agent Skills are ingested into the Skill Registry and loaded by the actor runtime via progressive disclosure. Inside actor graphs they appear as tool nodes whose execution is instruction-driven — the agent follows multi-step procedures that may call MCP tools, built-ins, or other Agent Skills. This adds portable, reusable workflows that complement schema-driven MCP tools. @@ -42896,24 +42998,24 @@ Each adopted standard maps to an explicit architectural boundary: #### ACP Integration Architecture !!! adr "Architecture Decision" - The Agent Client Protocol is defined in [ADR-026: Agent Client Protocol (ACP)](adr/ADR-026-agent-client-protocol.md). See also [Core Concepts > Server > ACP](#agent-client-protocol-acp) for the behavioral specification. + The Agent Client Protocol is defined in [ADR-026: Agent Client Protocol (ACP)](adr/ADR-026-agent-client-protocol.md). The adoption of the external ACP standard is defined in [ADR-047: ACP Standard Adoption](adr/ADR-047-acp-standard-adoption.md). See also [Core Concepts > Server > ACP](#agent-client-protocol-acp) for the behavioral specification. Of the four standards CleverAgents adopts, ACP has the deepest architectural integration. While MCP, Agent Skills, and LSP plug into the Infrastructure and Domain layers to provide capabilities to actors, **ACP defines the fundamental boundary between the Presentation and Application layers** — it is the protocol surface through which *every* client operation flows, in *every* deployment mode. This section provides the full architectural detail. ##### Why ACP Exists -The architecture faces a structural tension: the system has five Presentation-layer surfaces (CLI, TUI, Web, IDE plugin (embedded TUI), REST API) and two deployment modes (local, server). Without a shared contract: +The architecture faces a structural tension: the system has multiple Presentation-layer surfaces (CLI, TUI, Web, IDE plugin) and multiple deployment modes (local, server). Without a shared contract: 1. Each client would implement its own calling convention into the Application layer. 2. Local and server modes would develop behavioral drift. 3. Third-party client development would be impractical. -4. Testing would require N x 2 test matrices (N clients x 2 modes). +4. Testing would require N x M test matrices (N clients x M modes). -ACP resolves this by providing a **single, versioned operation surface** that all clients program against. The contract is the same whether the transport is an in-process Python call or an HTTPS request. This is the architectural guarantee that clients are interchangeable. +CleverAgents adopts the external **Agent Client Protocol** standard ([agentclientprotocol.org](https://agentclientprotocol.org)), built on **JSON-RPC 2.0**, as the sole protocol for all client-server communication. The contract is the same whether the transport is stdio (local) or HTTP (server). This is the architectural guarantee that clients are interchangeable. ##### ACP in the Layer Diagram -ACP sits at the boundary between the Presentation and Application layers. Every arrow from a Presentation-layer component to the Service Facade in the [High-Level Component Diagram](#high-level-component-diagram) represents an ACP operation: +ACP sits at the boundary between the Presentation and Application layers. Every arrow from a Presentation-layer component to the Service Facade in the [High-Level Component Diagram](#high-level-component-diagram) represents an ACP method call: ```kroki-plantuml @startuml @@ -42925,7 +43027,7 @@ package "Presentation Layer" #E0F7FA { component [TUI] as TUI component [Web] as Web component [IDE Plugin\n(Embedded TUI)] as IDE - component [REST API\n(FastAPI)] as REST + component [ACP Server Endpoint\n(JSON-RPC 2.0)] as ACPSRV } interface "ACP" as ACP_IF #FFD54F @@ -42940,203 +43042,421 @@ CLI -down-> ACP_IF TUI -down-> ACP_IF Web -down-> ACP_IF IDE -down-> ACP_IF -REST -down-> ACP_IF +ACPSRV -down-> ACP_IF ACP_IF -down-> SF ACP_IF -down-> SW ACP_IF -down-> EE note right of ACP_IF - **Local mode**: direct Python calls - **Server mode**: HTTPS + JSON - Same operation surface, same semantics + **Local mode**: ACP over stdio + **Server mode**: ACP over HTTP + JSON-RPC 2.0 wire format end note @enduml ``` -##### Transport Duality: Local Facade vs HTTPS +##### Transport Modes -The transport duality is the core architectural mechanism that makes ACP work across deployment modes: +The transport mechanism is the core architectural enabler that makes ACP work across deployment modes. The ACP Python SDK provides both transports: -**Local Mode (In-Process)** +**Local Mode — ACP over stdio** ``` -CLI ──→ ACPLocalTransport ──→ ServiceFacade.method() ──→ Domain / Infrastructure +CLI ──stdio──→ Agent Subprocess ──→ AcpLocalFacade ──→ ServiceFacade.method() ──→ Domain / Infrastructure ``` -The `ACPLocalTransport` is a thin adapter that translates ACP operation names to direct Python method calls on the Service Facade. There is no serialization, no network overhead, and no authentication. The adapter exists solely to enforce the ACP contract — ensuring that local clients cannot bypass the operation surface and call internal services directly. +The client spawns the agent as a subprocess. ACP JSON-RPC messages flow over stdin/stdout. Standard ACP methods drive the conversation (session/prompt, session/update notifications). Extension methods (`_cleveragents/*`) are intercepted by `AcpLocalFacade` and routed to in-process Application-layer services. No network, no authentication — the agent runs with local user permissions. -**Server Mode (HTTPS)** +**Server Mode — ACP over HTTP** ``` -CLI ──→ ACPHTTPTransport ──→ HTTPS POST ──→ FastAPI Router ──→ ServiceFacade.method() +CLI ──HTTP──→ CleverAgents ACP Server ──→ ServiceFacade.method() ──→ Domain / Infrastructure + │ │ + └── RemoteGraph.invoke() ──→ LangGraph Platform ────────┘ ``` -The `ACPHTTPTransport` serializes each operation into the canonical JSON request envelope, sends it to the server's REST API, and deserializes the JSON response envelope. Authentication is enforced via the `server.token` header. The FastAPI router maps URL paths to Service Facade methods using the same operation-to-service mapping as the local transport. +The client connects to the CleverAgents server via the ACP SDK's HTTP transport. All methods — both standard ACP and `_cleveragents/` extensions — flow through the **single ACP JSON-RPC 2.0 endpoint**. The server delegates actor execution to LangGraph Platform via RemoteGraph. Authentication is required via the ACP `authenticate` method. -This duality is enforced architecturally: +**External Agent Mode — Split Routing** -* **The same ACP test suite runs in both modes.** Contract tests validate that a given operation with given parameters produces the same response shape in local and server mode. +``` +CLI ──HTTP──→ External ACP Server (standard ACP methods: session/prompt, session/update, etc.) + ──HTTP──→ CleverAgents Server (_cleveragents/* extension methods only) +``` + +When an agent is hosted on an external ACP-compatible server, the client routes standard ACP methods to that server and `_cleveragents/` extension methods to the CleverAgents server. This enables interoperability with any ACP-compliant agent. + +This transport architecture is enforced by: + +* **The same ACP test suite runs in both modes.** Contract tests validate that a given method with given parameters produces the same response shape over stdio and HTTP. * **No Presentation-layer module imports from Infrastructure.** Architecture tests (import-linter) verify this on every commit, preventing clients from bypassing ACP. * **The Service Facade is the sole entry point.** No Application-layer service is exposed directly to the Presentation layer. -##### Complete Operation Routing +##### Complete Method Routing -Every ACP operation routes to a specific Application-layer service method. The following table is the authoritative mapping: +Every ACP method routes to a specific Application-layer service method. The following tables are the authoritative mapping. -**Session Operations** +**Standard ACP Methods → Service Mapping** -| ACP Operation | HTTP Verb + Path (Server) | Service Method | -| :------------ | :------------------------ | :------------- | -| `session.create` | `POST /api/v1/sessions` | `SessionWorkflow.create()` | -| `session.list` | `GET /api/v1/sessions` | `SessionWorkflow.list()` | -| `session.show` | `GET /api/v1/sessions/{id}` | `SessionWorkflow.show()` | -| `session.delete` | `DELETE /api/v1/sessions/{id}` | `SessionWorkflow.delete()` | -| `session.export` | `GET /api/v1/sessions/{id}/export` | `SessionWorkflow.export()` | -| `session.import` | `POST /api/v1/sessions/import` | `SessionWorkflow.import_session()` | -| `session.tell` | `POST /api/v1/sessions/{id}/tell` | `SessionWorkflow.tell()` | +| ACP Method | Service Method | +| :--------- | :------------- | +| `initialize` | Capability negotiation — returns supported extensions, automation profiles, tool registries | +| `authenticate` | Token validation against user/token store | +| `session/new` | `SessionWorkflow.create()` | +| `session/load` | `SessionWorkflow.resume()` | +| `session/list` | `SessionWorkflow.list()` | +| `session/prompt` | `SessionWorkflow.tell()` | +| `session/cancel` | `PlanService.cancel()` (cancels in-progress plan execution) | +| `session/set_mode` | `AutomationProfileService.switch()` | +| `session/set_model` | `ProviderRegistry.switch_model()` | +| `session/fork` | `SessionWorkflow.fork()` | +| `session/resume` | `SessionWorkflow.resume()` | -**Plan Operations** +**Plan Extension Methods → Service Mapping** -| ACP Operation | HTTP Verb + Path (Server) | Service Method | -| :------------ | :------------------------ | :------------- | -| `plan.use` | `POST /api/v1/plans` | `PlanService.create_plan()` | -| `plan.execute` | `POST /api/v1/plans/{id}/execute` | `PlanLifecycle.execute()` | -| `plan.apply` | `POST /api/v1/plans/{id}/apply` | `PlanLifecycle.apply()` | -| `plan.cancel` | `POST /api/v1/plans/{id}/cancel` | `PlanService.cancel()` | -| `plan.status` | `GET /api/v1/plans/{id}/status` | `PlanService.get_status()` | -| `plan.tree` | `GET /api/v1/plans/{id}/tree` | `PlanService.get_tree()` | -| `plan.explain` | `GET /api/v1/plans/{id}/decisions/{did}` | `PlanService.explain_decision()` | -| `plan.correct` | `POST /api/v1/plans/{id}/decisions/{did}/correct` | `CorrectionFlow.correct()` | -| `plan.diff` | `GET /api/v1/plans/{id}/diff` | `PlanService.get_diff()` | -| `plan.artifacts` | `GET /api/v1/plans/{id}/artifacts` | `PlanService.get_artifacts()` | -| `plan.prompt` | `POST /api/v1/plans/{id}/prompt` | `PlanService.inject_guidance()` | -| `plan.rollback` | `POST /api/v1/plans/{id}/rollback` | `PlanLifecycle.rollback()` | -| `plan.list` | `GET /api/v1/plans` | `PlanService.list()` | +| ACP Extension Method | Service Method | +| :------------------- | :------------- | +| `_cleveragents/plan/use` | `PlanService.create_plan()` | +| `_cleveragents/plan/execute` | `PlanLifecycle.execute()` | +| `_cleveragents/plan/apply` | `PlanLifecycle.apply()` | +| `_cleveragents/plan/cancel` | `PlanService.cancel()` | +| `_cleveragents/plan/status` | `PlanService.get_status()` | +| `_cleveragents/plan/tree` | `PlanService.get_tree()` | +| `_cleveragents/plan/explain` | `PlanService.explain_decision()` | +| `_cleveragents/plan/correct` | `CorrectionFlow.correct()` | +| `_cleveragents/plan/diff` | `PlanService.get_diff()` | +| `_cleveragents/plan/artifacts` | `PlanService.get_artifacts()` | +| `_cleveragents/plan/prompt` | `PlanService.inject_guidance()` | +| `_cleveragents/plan/rollback` | `PlanLifecycle.rollback()` | +| `_cleveragents/plan/list` | `PlanService.list()` | -**Registry Operations** (pattern applies to all registry types) +**Registry Extension Methods → Service Mapping** (pattern applies to all entity types) -| ACP Operation Pattern | HTTP Verb + Path Pattern (Server) | Service Method Pattern | -| :-------------------- | :-------------------------------- | :--------------------- | -| `{entity}.list` | `GET /api/v1/{entities}` | `{Entity}Service.list()` | -| `{entity}.show` | `GET /api/v1/{entities}/{name}` | `{Entity}Service.show()` | -| `{entity}.add` | `POST /api/v1/{entities}` | `{Entity}Service.add()` | -| `{entity}.update` | `PUT /api/v1/{entities}/{name}` | `{Entity}Service.update()` | -| `{entity}.remove` | `DELETE /api/v1/{entities}/{name}` | `{Entity}Service.remove()` | +| ACP Extension Method Pattern | Service Method Pattern | +| :--------------------------- | :--------------------- | +| `_cleveragents/registry/{entity}/list` | `{Entity}Service.list()` | +| `_cleveragents/registry/{entity}/show` | `{Entity}Service.show()` | +| `_cleveragents/registry/{entity}/add` | `{Entity}Service.add()` | +| `_cleveragents/registry/{entity}/update` | `{Entity}Service.update()` | +| `_cleveragents/registry/{entity}/remove` | `{Entity}Service.remove()` | Where `{entity}` is one of: `actor`, `skill`, `tool`, `validation`, `resource`, `resource_type`, `project`, `action`, `automation_profile`, `invariant`, `lsp`. -**Context Operations** +**Context Extension Methods → Service Mapping** -| ACP Operation | HTTP Verb + Path (Server) | Service Method | -| :------------ | :------------------------ | :------------- | -| `context.show` | `GET /api/v1/projects/{name}/context` | `ContextService.show()` | -| `context.inspect` | `GET /api/v1/projects/{name}/context/inspect` | `ContextService.inspect()` | -| `context.simulate` | `POST /api/v1/projects/{name}/context/simulate` | `ContextService.simulate()` | -| `context.set` | `PUT /api/v1/projects/{name}/context` | `ContextService.set()` | +| ACP Extension Method | Service Method | +| :------------------- | :------------- | +| `_cleveragents/context/show` | `ContextService.show()` | +| `_cleveragents/context/inspect` | `ContextService.inspect()` | +| `_cleveragents/context/simulate` | `ContextService.simulate()` | +| `_cleveragents/context/set` | `ContextService.set()` | -**Event Streaming** +**Sync, Namespace, and Health Extension Methods** -| ACP Operation | HTTP Path (Server) | Mechanism | -| :------------ | :----------------- | :-------- | -| `events.subscribe` | `GET /api/v1/events/stream` | SSE (Server-Sent Events) | +| ACP Extension Method | Service Method | +| :------------------- | :------------- | +| `_cleveragents/sync/pull` | `SyncService.pull()` | +| `_cleveragents/sync/push` | `SyncService.push()` | +| `_cleveragents/sync/status` | `SyncService.status()` | +| `_cleveragents/namespace/list` | `NamespaceService.list()` | +| `_cleveragents/namespace/show` | `NamespaceService.show()` | +| `_cleveragents/namespace/members` | `NamespaceService.members()` | +| `_cleveragents/health/check` | Health check handler | +| `_cleveragents/diagnostics/run` | Diagnostic runner | -##### Event Streaming Architecture +##### Streaming Architecture -ACP's event streaming provides real-time visibility into long-running operations. The architecture differs by deployment mode but delivers the same event semantics: +ACP streaming uses **JSON-RPC 2.0 notifications** (`session/update`) — messages with no `id` field that expect no response. The server pushes these to the client in real-time during long-running operations: -**Server Mode — Server-Sent Events (SSE)** +**Notification types and their CleverAgents mappings:** -Clients open a persistent HTTPS connection to `/api/v1/events/stream` with optional query parameters for filtering (`plan_id`, `event_type`). The server pushes structured JSON events as they occur: - -``` -event: plan.phase_changed -data: {"plan_id": "01HXM8C2...", "from_phase": "strategize", "to_phase": "execute", "timestamp": "2026-02-08T14:22:01Z"} - -event: tool.invoked -data: {"plan_id": "01HXM8C2...", "tool": "local/git-diff", "resource": "local/platform-repo", "timestamp": "2026-02-08T14:22:03Z"} - -event: validation.completed -data: {"plan_id": "01HXM8C2...", "validation": "local/pytest-runner", "passed": true, "timestamp": "2026-02-08T14:22:08Z"} -``` - -**Local Mode — In-Process Subscription** - -Clients subscribe to the `EventEmitter` directly via RxPY observables or callback registration. The same event types and payload shapes are emitted — the only difference is the delivery mechanism (in-process push vs HTTP streaming). - -**Event Types** - -| Event Type | Emitted When | Payload Keys | -| :--------- | :----------- | :----------- | -| `plan.created` | Plan instantiated from action | `plan_id`, `action`, `projects` | -| `plan.phase_changed` | Phase transition occurs | `plan_id`, `from_phase`, `to_phase` | -| `plan.state_changed` | State changes within a phase | `plan_id`, `phase`, `from_state`, `to_state` | -| `plan.decision_made` | Decision recorded | `plan_id`, `decision_id`, `decision_type`, `confidence` | -| `tool.invoked` | Tool execution begins | `plan_id`, `tool`, `resource`, `args` | -| `tool.completed` | Tool execution finishes | `plan_id`, `tool`, `result`, `duration_ms` | -| `validation.completed` | Validation finishes | `plan_id`, `validation`, `resource`, `passed`, `message` | -| `plan.apply_completed` | Apply phase finishes | `plan_id`, `terminal_state`, `files_changed` | -| `plan.error` | Unrecoverable error | `plan_id`, `error_code`, `message` | -| `session.message` | Orchestrator produces a message | `session_id`, `role`, `content` | - -**Causal Ordering Guarantee**: Events within a single plan lifecycle are delivered in causal order. A `plan.phase_changed` event for Execute always arrives after the `plan.phase_changed` event for Strategize completion. Events from different plans may interleave. - -##### Authentication and Authorization - -ACP authentication is deployment-mode-dependent: - -* **Local mode**: Authentication is bypassed entirely. The `auth` field in the ACP envelope is ignored. All operations execute as the local user. -* **Server mode**: Every request must include a valid token in the `auth.token` field (mapped to the `Authorization: Bearer ` header in HTTPS). The token is configured via `server.token` in the global configuration. Authorization is enforced per namespace — a user can only access entities within namespaces they have been granted access to. - -Unauthenticated server-mode requests receive a structured ACP error: +| Notification Type | Emitted When | Data Keys | +| :---------------- | :----------- | :-------- | +| `agent_message_chunk` | Agent produces response tokens | `content` | +| `plan` | Plan state changes | `entries` (list of `{content, priority, status}`) | +| `tool_call` | Tool execution begins | `tool_name`, `arguments` | +| `tool_call_update` | Tool execution progress/result | `tool_name`, `status`, `output` | +| `mode_change` | Automation profile switched | `mode` | +**Example notification:** ```json { - "acp_version": "1.0", - "request_id": null, - "status": "error", - "data": null, - "error": { "code": "AUTH_REQUIRED", "message": "Valid authentication token required" } + "jsonrpc": "2.0", + "method": "session/update", + "params": { + "session_id": "ses_01HXM8C2...", + "type": "tool_call", + "data": { "tool_name": "local/git-diff", "arguments": { "resource": "local/platform-repo" } } + } } ``` +**Causal Ordering Guarantee**: Notifications within a single session/plan lifecycle are delivered in causal order. A phase transition notification always arrives after the preceding phase completion. + +In **local mode** (stdio), notifications flow as JSON-RPC messages over stdout. In **server mode** (HTTP), the ACP SDK manages the streaming connection. Both modes deliver the same notification types with the same payload shapes. + +##### Authentication and Authorization + +Authentication follows the ACP standard connection lifecycle: + +1. **`initialize`**: Client sends capabilities (supported extensions, protocol version). Server responds with its capabilities (including `_cleveragents` extension catalog). +2. **`authenticate`**: Client sends `{ "token": "" }`. Server validates against its user/token store and returns authentication status. +3. **All subsequent methods**: Authenticated. The HTTP transport also sends the token as `Authorization: Bearer ` header on every request. + +**Local mode** (stdio): Authentication is bypassed entirely. The agent subprocess runs with the user's local permissions. The `authenticate` method is not called. + +**Server mode**: Every connection must complete `initialize` → `authenticate` before any other method. Unauthenticated requests receive a JSON-RPC error: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "error": { "code": -32002, "message": "Authentication required" } +} +``` + +**Authorization** is namespace-scoped: users can only access entities within namespaces they have been granted access to. The `local/` namespace is never accessible via server. + ##### Error Taxonomy -ACP defines a structured error code taxonomy that clients can handle programmatically: +ACP uses JSON-RPC 2.0 integer error codes. Standard protocol errors use the reserved range; application-specific errors use `-32001` to `-32099`: -| Error Code | HTTP Status | Meaning | -| :--------- | :---------- | :------ | -| `AUTH_REQUIRED` | 401 | Missing or expired authentication token | -| `AUTH_FORBIDDEN` | 403 | Valid token but insufficient namespace access | -| `NOT_FOUND` | 404 | Requested entity does not exist | -| `ALREADY_EXISTS` | 409 | Entity with this name/ID already registered | -| `INVALID_PARAMS` | 422 | Request parameters failed validation | -| `INVALID_STATE` | 409 | Operation not valid in the entity's current state (e.g., applying a plan that hasn't been executed) | -| `BUDGET_EXCEEDED` | 429 | Session or plan cost budget exceeded | -| `VERSION_MISMATCH` | 400 | Client ACP version is not supported by this server | -| `INTERNAL_ERROR` | 500 | Unexpected server-side failure | +| Error 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` | -All errors follow the same envelope structure with `code`, `message`, and optional `details`. Clients should dispatch on `code` rather than HTTP status for precise error handling. +All errors follow the JSON-RPC 2.0 error structure: `{ "code": , "message": , "data": }`. Clients should dispatch on `code` for precise error handling. -##### ACP Versioning Protocol +##### ACP Versioning -ACP uses semantic versioning to protect clients during protocol evolution: - -1. **Client advertises version**: Every request includes `acp_version` (e.g., `"1.0"`). -2. **Server validates compatibility**: The server supports the current minor version plus one prior minor version. A request with an unsupported version receives a `VERSION_MISMATCH` error. -3. **Additive changes within a major version**: New optional response fields, new operations, and new event types can be added in minor versions without breaking existing clients. -4. **Breaking changes require a major bump**: Removing fields, changing field types, or altering operation semantics requires a major version increment. The server may serve multiple major versions 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` — the client sends its supported version, the server confirms compatibility +- CleverAgents extension version is advertised in the `initialize` response capabilities under `_cleveragents.version` +- Backward-compatible additions (new optional params, new extension methods) are permitted within a major version; breaking changes require a major version bump +- Servers support the current extension version plus one prior minor version ##### ACP and the IDE Plugin The IDE plugin is an embedded TUI — essentially the same Textual-based interface used by the standalone TUI, but hosted inside an IDE environment. Like all Presentation-layer clients, it communicates with the backend exclusively through ACP. It does not access the Domain or Infrastructure layers directly — all plan operations, validation queries, and context lookups go through ACP. This means: -* The IDE plugin works identically in local and server mode (same ACP operations). -* IDE features stay exactly aligned with CLI/TUI/Web behavior (same contract). -* The IDE plugin subscribes to ACP events for real-time plan and session updates. +* The IDE plugin works identically in local mode (ACP over stdio to a subprocess) and server mode (ACP over HTTP). +* IDE features stay exactly aligned with CLI/TUI behavior (same ACP methods, same JSON-RPC 2.0 format). +* The IDE plugin receives `session/update` notifications for real-time plan and session updates. +* ACP callbacks (`fs/*`, `terminal/*`) allow the agent to interact with the IDE's workspace and integrated terminal. Note that the **Language Server Protocol (LSP)** in CleverAgents is unrelated to the IDE plugin. LSP serves an entirely different purpose: providing language intelligence to actors and agents in the Infrastructure layer. See [LSP Integration](#lsp-integration) and [ADR-027: Language Server Protocol (LSP) Integration](adr/ADR-027-language-server-protocol.md) for the full LSP architecture. +#### Server and Client Architecture + +!!! adr "Architecture Decision" + The server application architecture is defined in [ADR-048: Server Application Architecture](adr/ADR-048-server-application-architecture.md). The ACP standard adoption is defined in [ADR-047: ACP Standard Adoption](adr/ADR-047-acp-standard-adoption.md). + +This section defines the complete architecture of the CleverAgents server application and the client-side components that interact with it. The server is a **separate application** (distinct deployment unit) that shares the same Domain and Application layers as the client. + +##### Server Application Structure + +The server follows 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 | +| **Presentation** | CLI (Typer), TUI (Textual), IDE plugin | ACP JSON-RPC 2.0 endpoint | No | + +The shared Domain and Application layers are consumed as a Python package dependency. This ensures zero behavioral drift between local and server modes. + +```kroki-plantuml +@startuml +skinparam componentStyle rectangle +skinparam defaultFontSize 11 + +package "Client Application" #E0F7FA { + component [CLI / TUI / IDE Plugin] as ClientPres + component [Application Layer\n(shared)] as ClientApp + component [Domain Layer\n(shared)] as ClientDom + component [Infrastructure\n(SQLite, local sandbox,\nAcpLocalFacade)] as ClientInfra +} + +package "Server Application" #FFEBEE { + component [ACP JSON-RPC 2.0\nEndpoint] as ServerPres + component [Application Layer\n(shared)] as ServerApp + component [Domain Layer\n(shared)] as ServerDom + component [Infrastructure\n(PostgreSQL, RemoteGraph,\nACP SDK server)] as ServerInfra +} + +component [LangGraph\nPlatform] as LGP #FFF9C4 + +ClientPres -down-> ClientApp +ClientApp -down-> ClientDom +ClientDom -down-> ClientInfra + +ServerPres -down-> ServerApp +ServerApp -down-> ServerDom +ServerDom -down-> ServerInfra + +ServerInfra -right-> LGP : RemoteGraph + +ClientPres -right[hidden]-> ServerPres + +note bottom of ClientApp : Same Python package +note bottom of ServerApp : Same Python package +@enduml +``` + +##### Server Presentation Layer + +The server's sole client-facing interface is an **ACP JSON-RPC 2.0 endpoint** implemented using the ACP Python SDK's server-side connection handler. There is no REST API, no GraphQL, no separate admin endpoint. + +The endpoint handles three categories of messages: + +1. **Standard ACP methods** (`initialize`, `authenticate`, `session/*`): Routed to `SessionWorkflow` and actor execution (via LangGraph Platform RemoteGraph) +2. **Extension methods** (`_cleveragents/*`): Routed to Application-layer services (PlanService, RegistryServices, SyncService, NamespaceService, etc.) +3. **Callbacks to clients** (`session/request_permission`, `fs/*`, `terminal/*`): Forwarded from executing actors back to the connected client — enabling server-hosted agents to access client-local resources + +##### Server Infrastructure: LangGraph Platform + +Actor graphs (StateGraphs defined in YAML) are **deployed to LangGraph Platform** as separate deployments. The server invokes them via **RemoteGraph**: + +```kroki-mermaid +sequenceDiagram + participant C as Client + participant S as ACP Server + participant SW as SessionWorkflow + participant RG as RemoteGraph + participant LG as LangGraph Platform + + C->>S: session/prompt {"message": "Add pagination"} + S->>SW: tell("Add pagination") + SW->>RG: invoke(strategy_actor, state) + RG->>LG: Execute strategy actor graph + LG-->>RG: Strategy result (plan entries) + RG-->>SW: Plan with decisions + SW-->>S: session/update (plan notification) + S-->>C: session/update (streaming) + SW->>RG: invoke(execution_actor, state) + RG->>LG: Execute execution actor graph + LG-->>RG: Execution result + RG-->>SW: Completed execution + SW-->>S: session/update (tool_call notifications) + S-->>C: session/update (streaming) +``` + +Different actors for different plan phases (strategy, execution, estimation) each deploy as **separate RemoteGraphs**, enabling independent scaling without limiting plan capabilities. The `SessionWorkflow` orchestrates them the same way it orchestrates local actor graphs, but through the RemoteGraph interface. + +##### Server Infrastructure: 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 | + +Server-specific tables (authentication, namespace ACLs, user management) are additive — they extend the shared schema without modifying it. + +##### Server Security Architecture + +**Authentication**: Uses the ACP standard `initialize` → `authenticate` flow. Tokens are validated against the server's user/token store. HTTP transport also carries `Authorization: Bearer ` header. + +**Authorization**: Namespace-scoped. Users can only access entities in namespaces they have been granted access to. The `local/` namespace is never accessible via server. + +**Transport security**: HTTPS required for all production deployments (TLS termination at Kubernetes ingress). + +##### Client-Side Architecture + +The client uses an `AcpClient` that wraps the ACP Python SDK and routes method calls through a `TransportSelector`: + +```python +# Conceptual client architecture (not literal implementation) +class TransportSelector: + """Selects transport based on configuration.""" + + def get_transport(self) -> AcpTransport: + if config.server_url: + return AcpRemoteTransport(url=config.server_url, token=config.server_token) + else: + return AcpStdioTransport(agent_command=["cleveragents", "agent", "serve"]) + +class AcpClient: + """Unified client for all ACP communication.""" + + def __init__(self, transport: AcpTransport): + self.connection = ClientSideConnection(transport) + + async def prompt(self, session_id: str, message: str) -> AsyncIterator[Notification]: + """Send a prompt and stream notifications.""" + return await self.connection.request("session/prompt", { + "session_id": session_id, + "message": message, + }) + + async def plan_status(self, plan_id: str) -> dict: + """Get plan status via extension method.""" + return await self.connection.request("_cleveragents/plan/status", { + "plan_id": plan_id, + }) +``` + +**Evolution of existing ACP source code:** + +| Current Component | Becomes | Notes | +| :---------------- | :------ | :---- | +| `AcpLocalFacade` | **Retained** | Adapts to ACP method signatures; handles `_cleveragents/` dispatch to in-process services | +| `AcpHttpTransport` (stub) | **Replaced** by ACP SDK `ClientSideConnection` over HTTP | SDK provides production transport | +| `AcpRequest` / `AcpResponse` | **Deprecated** | Replaced by ACP SDK native JSON-RPC types | +| `AcpEvent` | **Mapped** to `session/update` notification types | Event types map to notification `type` field | +| `AcpEventQueue` | **Retained** for local mode | Delivers events as in-process JSON-RPC notifications | +| `AcpVersionNegotiator` | **Replaced** by `initialize` handshake | Capability negotiation per ACP standard | + +##### Local/Server Interchangeability + +The transport selector pattern ensures that application code is transport-agnostic. A CLI command works identically regardless of whether the backend is local or remote: + +```python +# CLI command — identical in local and server mode +async def plan_status_command(plan_id: str): + client = get_acp_client() # TransportSelector picks stdio or HTTP + result = await client.plan_status(plan_id) + render_plan_status(result) +``` + +The only difference is where computation happens: + +| Aspect | Local Mode | Server Mode | +| :----- | :--------- | :---------- | +| Transport | ACP over stdio | ACP over HTTP | +| Actor execution | In-process LangGraph | LangGraph Platform RemoteGraph | +| Database | SQLite | PostgreSQL | +| Authentication | Bypassed | `authenticate` + Bearer token | +| File/terminal access | Direct | Via ACP callbacks (server → client) | +| Namespace resolution | `local/` only | `local/` + server namespaces | + +##### Server Deployment + +| 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 to ACP endpoint | +| Database | PostgreSQL (managed or self-hosted) | External to the application container | +| LangGraph Platform | Managed or self-hosted | External service for graph execution | +| Caching | Redis (optional) | Multi-instance session affinity, rate limiting | + #### Architectural Principles 1. **Hexagonal Architecture (Ports and Adapters)**: The domain layer defines abstract repository interfaces (ports). The infrastructure layer provides concrete implementations (adapters). This allows swapping SQLite for PostgreSQL, FAISS for Qdrant, or local execution for remote server execution without touching domain logic. @@ -43308,7 +43628,7 @@ This section enumerates every technology choice in the CleverAgents stack, organ | **dependency-injector** | >= 4.41.0 | DI container | `DeclarativeContainer` with `Singleton`, `Factory`, and `Configuration` providers. Wires all services, repositories, and infrastructure components. | | **watchdog** | >= 4.0.0 | File system monitoring | Watches project resource directories for changes. Triggers automatic re-indexing when `index.auto-reindex` is enabled. | | **numpy** | >= 2.1.0 | Numerical computing | Vector operations for embedding similarity computation, confidence score aggregation, and distance calculations in the vector store layer. | -| **uvicorn** | >= 0.30.1 | ASGI server | HTTP server for server mode REST API. Serves the FastAPI application for remote plan execution, namespace resolution, and multi-user collaboration. | +| **uvicorn** | >= 0.30.1 | ASGI server | HTTP server for server mode ACP endpoint. Hosts the ACP JSON-RPC 2.0 server for remote plan execution, namespace resolution, and multi-user collaboration. | ### Dependency Injection diff --git a/mkdocs.yml b/mkdocs.yml index ef0f0ce3..5502f295 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -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