Files
cleveragents-core/docs/api/a2a-protocol.md
T
HAL9000 2b0bc485a4
CI / lint (pull_request) Successful in 30s
CI / typecheck (pull_request) Successful in 55s
CI / quality (pull_request) Successful in 52s
CI / security (pull_request) Successful in 1m5s
CI / build (pull_request) Successful in 23s
CI / helm (pull_request) Successful in 34s
CI / push-validation (pull_request) Successful in 20s
CI / e2e_tests (pull_request) Successful in 4m54s
CI / integration_tests (pull_request) Successful in 5m34s
CI / unit_tests (pull_request) Successful in 6m34s
CI / docker (pull_request) Successful in 16s
CI / coverage (pull_request) Successful in 13m26s
CI / status-check (pull_request) Successful in 1s
docs(a2a): add A2A protocol, guards, and automation profile documentation (v3.5.0)
- Add docs/api/a2a-protocol.md with JSON-RPC 2.0 wire format, facade session
  lifecycle, event queue publish/subscribe, stdio and HTTP transport modes,
  extension methods (_cleveragents/ namespace), standard A2A operations,
  Agent Card discovery, and CLI commands
- Add docs/api/guards.md with guard enforcement documentation covering denylist,
  allowlist, budget caps, tool call limits, write/apply approval gates,
  AutonomyGuardrailService, budget hierarchy, and GuardrailAuditTrail
- Add docs/api/automation-profiles.md with profile resolution precedence
  (plan > action > project > global), eight built-in profiles, confidence
  threshold semantics, safety profile sub-model, Python API, and CLI commands
- Update mkdocs.yml navigation to include new API docs
- Update CHANGELOG.md with documentation entries

Refs: #9010
2026-04-14 05:45:47 +00:00

19 KiB

A2A Protocol — Agent-to-Agent Communication

Version: v3.5.0 / v3.8.0 (JSON-RPC 2.0 wire format) ADRs: ADR-026 · ADR-047

The Agent-to-Agent (A2A) Protocol is the sole communication boundary between all CleverAgents clients (CLI, TUI, IDE plugin) and the Application-layer services. Every operation — from creating a session to executing a plan — flows through A2A. No Presentation-layer module is permitted to bypass A2A and access Domain or Infrastructure services directly.

CleverAgents adopts the external A2A open standard (Linux Foundation, Apache 2.0) built on JSON-RPC 2.0, with platform-specific operations exposed via the _cleveragents/ extension namespace.


Overview

+------------------------------------------------------------------+
|  Presentation Layer                                              |
|  CLI · TUI · IDE Plugin                                          |
+--------------------------------+---------------------------------+
                                 |  A2A (JSON-RPC 2.0)
                                 |  stdio (local) or HTTP (server)
+--------------------------------v---------------------------------+
|  A2A Boundary                                                    |
|  A2aLocalFacade (local) · A2A SDK HTTP transport (server)        |
+--------------------------------+---------------------------------+
                                 |
+--------------------------------v---------------------------------+
|  Application Layer                                               |
|  SessionService · PlanLifecycleService · ToolRegistry · ...      |
+------------------------------------------------------------------+

JSON-RPC 2.0 Wire Format

All A2A messages use the JSON-RPC 2.0 envelope.

Request

{
  "jsonrpc": "2.0",
  "id": "01HXRCF1ABCDE12345678901AB",
  "method": "_cleveragents/plan/status",
  "params": {
    "plan_id": "01HXRCF1ABCDE12345678901AB"
  }
}
Field Type Description
jsonrpc "2.0" Always the literal string "2.0"
id string Request correlation ID (ULID recommended); auto-generated if omitted
method string Operation name — standard A2A or _cleveragents/ extension
params object Operation parameters (may be empty {})

Successful Response

{
  "jsonrpc": "2.0",
  "id": "01HXRCF1ABCDE12345678901AB",
  "result": {
    "plan_id": "01HXRCF1ABCDE12345678901AB",
    "phase": "execute",
    "state": "running"
  }
}

Error Response

{
  "jsonrpc": "2.0",
  "id": "01HXRCF1ABCDE12345678901AB",
  "error": {
    "code": -32001,
    "message": "Plan not found",
    "data": { "plan_id": "01HXRCF1ABCDE12345678901AB" }
  }
}

result and error are mutually exclusive — exactly one must be present.

Error Codes

Code Meaning Domain Exception
-32700 Parse error (malformed JSON)
-32600 Invalid request
-32601 Method not found A2aOperationNotFoundError
-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 A2aVersionMismatchError
-32008 Plan error PlanError

Application-specific codes use the range -32001 to -32099.


Python Models

from cleveragents.a2a.models import A2aRequest, A2aResponse, A2aErrorDetail, A2aEvent

A2aRequest

class A2aRequest(BaseModel):
    jsonrpc: str = "2.0"          # Always "2.0"; validated
    id: str = ""                   # Auto-generated ULID if empty
    method: str                    # Required; must not be blank
    params: dict[str, Any] = {}

A2aResponse

class A2aResponse(BaseModel):
    jsonrpc: str = "2.0"
    id: str                        # Echoes the request id
    result: dict[str, Any] | None = None   # Success path
    error: A2aErrorDetail | None = None    # Error path
    # Exactly one of result/error must be set (enforced by validator)

A2aErrorDetail

class A2aErrorDetail(BaseModel):
    code: int        # JSON-RPC 2.0 integer error code
    message: str     # Human-readable description
    data: dict[str, Any] = {}   # Optional structured context

A2aEvent

class A2aEvent(BaseModel):
    event_id: str        # Auto-generated ULID
    event_type: str      # e.g. "TaskStatusUpdateEvent"
    plan_id: str | None = None
    data: dict[str, Any] = {}
    timestamp: str       # ISO-8601 UTC; auto-set

Breaking change (v3.7.0+): Fields were renamed to comply with JSON-RPC 2.0. a2a_versionjsonrpc, request_idid, operationmethod, data (success) → result. See CHANGELOG.


Transport Modes

A2A is transport-agnostic. CleverAgents supports two transports with identical operational semantics.

Local Mode — JSON-RPC 2.0 over stdio

The client spawns the agent as a subprocess. JSON-RPC messages flow over stdin/stdout. The A2aLocalFacade routes operations to in-process Application-layer services. No network, no authentication.

CLI Process --stdin/stdout--> Agent Subprocess
                                  |
                                  +-- A2aLocalFacade --> ServiceFacade
from cleveragents.a2a import A2aLocalFacade, A2aRequest

facade = A2aLocalFacade(services={
    "session_service": session_svc,
    "plan_lifecycle_service": plan_svc,
    "tool_registry": tool_registry,
    "event_queue": event_queue,
})

response = facade.dispatch(
    A2aRequest(method="session.create", params={"actor_name": "openai/gpt-4o"})
)
session_id = response.result["session_id"]

Server Mode — JSON-RPC 2.0 over HTTP

The client connects to the CleverAgents server via the A2A SDK HTTP transport. Authentication uses the security schemes declared in the Agent Card.

CLI --HTTP--> CleverAgents A2A Server --> ServiceFacade

The A2A-Version HTTP header communicates the protocol version. The Agent Card at /.well-known/agent.json advertises supported security schemes (OAuth2, API key, HTTP bearer).


A2aLocalFacade — Session Lifecycle

The A2aLocalFacade is the primary entry point in local mode.

Constructor

from cleveragents.a2a.facade import A2aLocalFacade

facade = A2aLocalFacade(services={
    "session_service":           session_service,       # SessionService
    "plan_lifecycle_service":    plan_lifecycle_service, # PlanLifecycleService
    "tool_registry":             tool_registry,          # ToolRegistry
    "resource_registry_service": resource_registry_svc, # ResourceRegistryService
    "event_queue":               event_queue,            # A2aEventQueue
})

All service keys are optional. Missing services return safe stub responses so the facade never crashes due to incomplete wiring.

Session Create

response = facade.dispatch(A2aRequest(
    method="session.create",
    params={"actor_name": "openai/gpt-4o"}
))
# response.result == {"session_id": "01HXRCF1...", "status": "created"}

If session_id is already supplied in params, the call is idempotent and returns the existing ID without creating a duplicate (fixes Forgejo #1141).

Session Close

response = facade.dispatch(A2aRequest(
    method="session.close",
    params={"session_id": "01HXRCF1..."}
))
# response.result == {"status": "closed"}

Session close automatically stops any active devcontainers associated with the session (best-effort; failures are logged but never propagated).

Plan Lifecycle via Extension Methods

# Create (use) a plan
response = facade.dispatch(A2aRequest(
    method="_cleveragents/plan/use",
    params={"action_name": "refactor-auth", "arguments": {"target": "src/auth"}}
))
plan_id = response.result["plan_id"]

# Execute the plan
response = facade.dispatch(A2aRequest(
    method="_cleveragents/plan/execute",
    params={"plan_id": plan_id}
))

# Check status
response = facade.dispatch(A2aRequest(
    method="_cleveragents/plan/status",
    params={"plan_id": plan_id}
))
# response.result == {"plan_id": "...", "phase": "execute", "state": "running"}

# Apply the plan
response = facade.dispatch(A2aRequest(
    method="_cleveragents/plan/apply",
    params={"plan_id": plan_id}
))

Extension Methods (_cleveragents/)

CleverAgents platform operations use the _cleveragents/ namespace per the A2A extensibility mechanism (declared in the Agent Card under urn:cleveragents:extensions:v1).

Plan Lifecycle

Method Required Params Description
_cleveragents/plan/use action_name Create a plan from an action template
_cleveragents/plan/execute plan_id Transition plan to Execute phase
_cleveragents/plan/apply plan_id Transition plan to Apply phase
_cleveragents/plan/cancel plan_id Cancel a running plan
_cleveragents/plan/status plan_id Query plan phase and state
_cleveragents/plan/tree plan_id Retrieve the decision tree
_cleveragents/plan/explain plan_id, decision_id Explain a decision
_cleveragents/plan/correct plan_id Inject a correction
_cleveragents/plan/diff plan_id Retrieve plan diff
_cleveragents/plan/artifacts plan_id List plan artifacts
_cleveragents/plan/prompt plan_id, guidance Inject guidance into execution
_cleveragents/plan/rollback plan_id Roll back plan changes
_cleveragents/plan/list (none) List all plans

Registry

Method Optional Params Description
_cleveragents/registry/tool/list namespace List available tools
_cleveragents/registry/resource/list type_name List registered resources
_cleveragents/registry/actor/list List registered actors
_cleveragents/registry/skill/list List registered skills
_cleveragents/registry/action/list List registered actions
_cleveragents/registry/project/list List registered projects

Context

Method Description
_cleveragents/context/show Show assembled context
_cleveragents/context/inspect Inspect context tiers
_cleveragents/context/simulate Simulate context assembly
_cleveragents/context/set Override context values

Health & Diagnostics

Method Description
_cleveragents/health/check Health check
_cleveragents/diagnostics/run Run diagnostics

Sync & Namespace (stubs)

Method Description
_cleveragents/sync/pull Pull remote changes
_cleveragents/sync/push Push local changes
_cleveragents/sync/status Sync status
_cleveragents/namespace/list List namespaces
_cleveragents/namespace/show Show namespace detail
_cleveragents/namespace/members List namespace members

Legacy Operations (Deprecated)

The following proprietary operation names are kept for backward compatibility but are deprecated in favour of the _cleveragents/ extension methods:

Legacy Name Replacement
session.create session.create (unchanged)
session.close session.close (unchanged)
plan.create _cleveragents/plan/use
plan.execute _cleveragents/plan/execute
plan.status _cleveragents/plan/status
plan.diff _cleveragents/plan/diff
plan.apply _cleveragents/plan/apply
registry.list_tools _cleveragents/registry/tool/list
registry.list_resources _cleveragents/registry/resource/list
context.get _cleveragents/context/show
event.subscribe _cleveragents/event/subscribe

Event Queue — Publish / Subscribe

The A2aEventQueue provides in-memory pub/sub for plan and session events in local mode. In server mode, events are delivered via Server-Sent Events (SSE).

from cleveragents.a2a.events import A2aEventQueue, TASK_STATUS_UPDATE
from cleveragents.a2a.models import A2aEvent

queue = A2aEventQueue()

# Subscribe with a callback
def on_event(event: A2aEvent) -> None:
    print(f"[{event.event_type}] plan={event.plan_id} data={event.data}")

sub_id = queue.subscribe_local(on_event)

# Publish an event
queue.publish(A2aEvent(
    event_type=TASK_STATUS_UPDATE,
    plan_id="01HXRCF1...",
    data={"state": "working", "phase": "execute"},
))

# Retrieve recent events
events = queue.get_events(limit=50)

# Unsubscribe
queue.unsubscribe(sub_id)

# Shutdown
queue.close()

Event Types

Constant Value Emitted When
TASK_STATUS_UPDATE "TaskStatusUpdateEvent" Plan phase or state changes
TASK_ARTIFACT_UPDATE "TaskArtifactUpdateEvent" Agent produces output artifacts

SSE Wire Format

In server mode, events are formatted as Server-Sent Events with a JSON-RPC 2.0 notification payload:

event: TaskStatusUpdateEvent
id: 01HXRCF1ABCDE12345678901AB
data: {"jsonrpc":"2.0","method":"task/statusUpdate","params":{"taskId":"01HXRCF1...","state":"working"}}

EventBusBridge

The EventBusBridge subscribes to the internal domain EventBus and translates domain events into A2aEvent instances:

from cleveragents.a2a.events import EventBusBridge

bridge = EventBusBridge(event_bus=event_bus, event_queue=queue)
bridge.start()   # Subscribe to domain events
# ... plan execution ...
bridge.stop()    # Unsubscribe

Domain events mapped to TaskStatusUpdateEvent: PLAN_CREATED, PLAN_PHASE_CHANGED, PLAN_STATE_CHANGED, PLAN_APPLIED, PLAN_CANCELLED, PLAN_ERRORED.

Domain events mapped to TaskArtifactUpdateEvent: CHECKPOINT_RESTORED.


Standard A2A Operations

In addition to the _cleveragents/ extension methods, CleverAgents supports the standard A2A operations defined by the external specification:

Operation Direction Purpose
message/send Client → Server Send a message to the agent; receive a Task
message/stream Client → Server Send a message with SSE streaming of task updates
tasks/get Client → Server Retrieve current state of a task
tasks/list Client → Server List tasks with optional filtering
tasks/cancel Client → Server Cancel a running task
tasks/subscribe Client → Server Subscribe to task updates via SSE
pushNotificationConfig/* Client → Server Manage push notification webhooks
getExtendedAgentCard Client → Server Fetch authenticated Agent Card

Task Lifecycle States

State Description
submitted Task received, not yet started
working Task actively executing
input-required Task paused, awaiting human input (automation profile gate)
completed Task finished successfully
failed Task terminated with an error
canceled Task was explicitly cancelled
rejected Task was rejected before starting

Multi-Turn Interactions

When a plan's automation profile requires human approval, the Task enters input-required state. The client resumes execution by sending a message/send with the task's context ID:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "message/send",
  "params": {
    "message": {
      "role": "user",
      "parts": [{ "text": "Approved — proceed with the apply phase." }],
      "contextId": "task_01HXRCF1..."
    }
  }
}

Agent Card Discovery

In server mode, the Agent Card is served at /.well-known/agent.json and declares CleverAgents capabilities:

{
  "name": "CleverAgents",
  "description": "Autonomous software engineering agent",
  "skills": ["plan-lifecycle", "registry-crud", "context-management"],
  "extensions": [
    {
      "uri": "urn:cleveragents:extensions:v1",
      "methods": ["_cleveragents/plan/*", "_cleveragents/registry/*"]
    }
  ],
  "securitySchemes": {
    "bearerAuth": { "type": "http", "scheme": "bearer" }
  }
}

An authenticated extended Agent Card with additional detail is available via getExtendedAgentCard.


CLI Commands

Every CLI command maps to an A2A operation. The CLI is a thin rendering layer over A2A.

# Session management
agents a2a session create --actor openai/gpt-4o
agents a2a session list
agents a2a session close <SESSION_ID>

# Plan lifecycle
agents plan use <ACTION_NAME> [--automation-profile supervised]
agents plan execute <PLAN_ID>
agents plan status <PLAN_ID>
agents plan diff <PLAN_ID>
agents plan apply <PLAN_ID>
agents plan cancel <PLAN_ID>
agents plan tree <PLAN_ID>
agents plan correct <PLAN_ID> --guidance "Use dependency injection"
agents plan rollback <PLAN_ID>
agents plan list

# Registry
agents tool list [--namespace <NS>]
agents resource list [--type <TYPE>]
agents actor list
agents skill list

# Health
agents health check
agents diagnostics run

Versioning

Aspect Mechanism
JSON-RPC version Always "2.0" in the jsonrpc field
A2A protocol version A2A-Version HTTP header (server mode)
Extension version urn:cleveragents:extensions:v1 in Agent Card
Backward compatibility Servers support current + one prior minor version
Breaking changes Require major version bump of the extension namespace

Error Types

Exception Description
A2aError Base A2A exception
A2aNotAvailableError Operation not available in current mode
A2aOperationNotFoundError Unknown operation name
A2aVersionMismatchError Incompatible protocol versions
from cleveragents.a2a.errors import A2aOperationNotFoundError

try:
    response = facade.dispatch(request)
except A2aOperationNotFoundError as exc:
    print(f"Unknown method: {exc.operation}")

See Also